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
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
- --no-enable-prefix-caching
- --block-size
- "128"
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
command:
- python3
- -m
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def test_convert_vllm_disagg_decode_removes_disaggregation_role() -> None:
"--disaggregation-mode",
"decode",
"--kv-transfer-config",
'{"kv_connector":"NixlConnector","kv_role":"kv_both"}',
'{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}',
]
)

Expand All @@ -242,7 +242,7 @@ def test_convert_vllm_disagg_decode_removes_disaggregation_role() -> None:
assert "--disaggregation-mode" not in converted_args
assert not any(arg.startswith("--disaggregation-mode=") for arg in converted_args)
assert converted_args[converted_args.index("--kv-transfer-config") + 1] == (
'{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
'{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}'
)


Expand All @@ -264,28 +264,34 @@ def test_build_dgd_config_vllm_disagg_restores_runtime_args() -> None:
assert prefill_args[prefill_args.index("--disaggregation-mode") + 1] == "prefill"
assert (
prefill_args[prefill_args.index("--kv-transfer-config") + 1]
== '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
== '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
)
assert decode_args[decode_args.index("--tensor-parallel-size") + 1] == "4"
assert decode_args[decode_args.index("--disaggregation-mode") + 1] == "decode"
assert "--kv-transfer-config" not in decode_args
assert (
decode_args[decode_args.index("--kv-transfer-config") + 1]
== '{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}'
)


def test_build_dgd_config_vllm_disagg_preserves_explicit_kv_config() -> None:
"""An explicit connector remains authoritative while worker roles are canonical."""
custom_kv_config = (
'{"kv_connector":"NixlConnector","kv_role":"kv_both","kv_buffer_device":"cpu"}'
)
custom_prefill_kv_config = '{"kv_connector":"NixlConnector","kv_role":"kv_producer","kv_buffer_device":"cpu"}'
custom_decode_kv_config = '{"kv_connector":"NixlConnector","kv_role":"kv_consumer","kv_buffer_device":"cpu"}'
modifier = CONFIG_MODIFIERS["vllm"]
dgd_config = modifier.build_dgd_config(
mode="disagg",
model_name="test/model",
image="example/vllm:test",
prefill_cli_args=[
"--disaggregation-mode=decode",
f"--kv-transfer-config '{custom_kv_config}'",
f"--kv-transfer-config '{custom_prefill_kv_config}'",
],
decode_cli_args=[
"--disaggregation-mode",
"prefill",
f"--kv-transfer-config '{custom_decode_kv_config}'",
],
decode_cli_args=["--disaggregation-mode", "prefill"],
)

prefill_args = next(
Expand All @@ -303,10 +309,16 @@ def test_build_dgd_config_vllm_disagg_preserves_explicit_kv_config() -> None:
assert prefill_args[prefill_args.index("--disaggregation-mode") + 1] == "prefill"
assert prefill_args.count("--kv-transfer-config") == 1
assert (
prefill_args[prefill_args.index("--kv-transfer-config") + 1] == custom_kv_config
prefill_args[prefill_args.index("--kv-transfer-config") + 1]
== custom_prefill_kv_config
)
assert decode_args.count("--disaggregation-mode") == 1
assert decode_args[decode_args.index("--disaggregation-mode") + 1] == "decode"
assert decode_args.count("--kv-transfer-config") == 1
assert (
decode_args[decode_args.index("--kv-transfer-config") + 1]
== custom_decode_kv_config
)


def test_build_dgd_config_vllm_disagg_removes_legacy_role_flags() -> None:
Expand Down
12 changes: 6 additions & 6 deletions components/src/dynamo/profiler/utils/config_modifiers/vllm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

DEFAULT_VLLM_KV_TRANSFER_CONFIG = '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
DEFAULT_VLLM_KV_TRANSFER_CONFIG = {
SubComponentType.PREFILL: '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}',
SubComponentType.DECODE: '{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}',
}


def _get_valued_arg(args: list[str], key: str) -> str | None:
Expand Down Expand Up @@ -99,14 +102,11 @@ def _finalize_disagg_cli_args(args: list[str], role: SubComponentType) -> list[s
finalized = set_unique_argument_value(
cleaned_args, "--disaggregation-mode", role.value
)
if (
role == SubComponentType.PREFILL
and _get_valued_arg(finalized, "--kv-transfer-config") is None
):
if _get_valued_arg(finalized, "--kv-transfer-config") is None:
finalized = set_unique_argument_value(
finalized,
"--kv-transfer-config",
DEFAULT_VLLM_KV_TRANSFER_CONFIG,
DEFAULT_VLLM_KV_TRANSFER_CONFIG[role],
)
return finalized

Expand Down
17 changes: 12 additions & 5 deletions components/src/dynamo/vllm/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def update_dynamo_config_with_engine(
"When using --disaggregation-mode prefill, you must explicitly "
"provide --kv-transfer-config. Example:\n"
" --kv-transfer-config "
'\'{"kv_connector":"NixlConnector","kv_role":"kv_both"}\''
'\'{"kv_connector":"NixlConnector","kv_role":"kv_producer"}\''
)

# Clear connector list (no longer used for vLLM)
Expand Down Expand Up @@ -563,7 +563,7 @@ def _uses_dynamo_connector(engine_config: AsyncEngineArgs) -> bool:
return False


def _connector_to_kv_transfer_json(connectors: list[str]) -> str:
def _connector_to_kv_transfer_json(connectors: list[str], nixl_role: str) -> str:
"""Convert a legacy --connector list to the equivalent --kv-transfer-config JSON.

Used in error messages to help users migrate.
Expand All @@ -581,7 +581,7 @@ def _connector_to_kv_transfer_json(connectors: list[str]) -> str:
)
elif c == "nixl":
multi_connectors.append(
{"kv_connector": "NixlConnector", "kv_role": "kv_both"}
{"kv_connector": "NixlConnector", "kv_role": nixl_role}
)
elif c == "kvbm":
multi_connectors.append(
Expand Down Expand Up @@ -630,9 +630,16 @@ def _reject_connector_flag(dynamo_config: Config) -> None:
"no connector. Simply remove the --connector flag."
)

if dynamo_config.disaggregation_mode == DisaggregationMode.PREFILL:
nixl_role = "kv_producer"
elif dynamo_config.disaggregation_mode == DisaggregationMode.DECODE:
nixl_role = "kv_consumer"
else:
nixl_role = "kv_both"

# Active connectors: show migration path
if normalized:
equiv = _connector_to_kv_transfer_json(normalized)
equiv = _connector_to_kv_transfer_json(normalized, nixl_role)
raise ValueError(
"--connector is no longer supported for the vLLM backend. "
"Use --kv-transfer-config instead.\n"
Expand All @@ -643,7 +650,7 @@ def _reject_connector_flag(dynamo_config: Config) -> None:
if env_connector is not None:
env_values = [v.strip().lower() for v in env_connector.split() if v.strip()]
if env_values and not all(v in ("none", "null") for v in env_values):
equiv = _connector_to_kv_transfer_json(env_values)
equiv = _connector_to_kv_transfer_json(env_values, nixl_role)
raise ValueError(
"The DYN_CONNECTOR environment variable is no longer supported "
"for the vLLM backend. Use --kv-transfer-config instead.\n"
Expand Down
44 changes: 32 additions & 12 deletions components/src/dynamo/vllm/tests/test_vllm_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def test_endpoint_overrides_with_prefill_worker(mock_vllm_cli):
"--disaggregation-mode",
"prefill",
"--kv-transfer-config",
'{"kv_connector":"NixlConnector","kv_role":"kv_both"}',
'{"kv_connector":"NixlConnector","kv_role":"kv_producer"}',
)
config = parse_args()
assert config.namespace == "custom"
Expand Down Expand Up @@ -237,13 +237,31 @@ def test_removed_multimodal_role_flags_are_rejected(flag, mock_vllm_cli):
# --connector removal tests


def test_connector_nixl_raises_error_with_migration_hint(mock_vllm_cli):
"""Test that --connector nixl raises ValueError with --kv-transfer-config hint."""
def test_connector_nixl_raises_error_without_mode(mock_vllm_cli):
mock_vllm_cli("--model", "Qwen/Qwen3-0.6B", "--connector", "nixl")
with pytest.raises(ValueError, match="--connector is no longer supported"):
parse_args()


@pytest.mark.parametrize(
("mode", "expected_role"),
[("prefill", "kv_producer"), ("decode", "kv_consumer")],
)
def test_connector_nixl_migration_hint_uses_disaggregation_role(
mock_vllm_cli, mode, expected_role
):
mock_vllm_cli(
"--model",
"Qwen/Qwen3-0.6B",
"--connector",
"nixl",
"--disaggregation-mode",
mode,
)
with pytest.raises(ValueError, match=f'"kv_role": "{expected_role}"'):
parse_args()


def test_connector_none_raises_error(mock_vllm_cli):
"""Test that --connector none raises ValueError telling user it's no longer needed."""
mock_vllm_cli("--model", "Qwen/Qwen3-0.6B", "--connector", "none")
Expand Down Expand Up @@ -294,18 +312,20 @@ def test_prefill_worker_without_kv_transfer_config_raises(mock_vllm_cli):

def test_connector_to_kv_transfer_json_single():
"""Test _connector_to_kv_transfer_json returns valid JSON for a single connector."""
result = json.loads(_connector_to_kv_transfer_json(["nixl"]))
assert result == {"kv_connector": "NixlConnector", "kv_role": "kv_both"}
result = json.loads(_connector_to_kv_transfer_json(["nixl"], "kv_producer"))
assert result == {"kv_connector": "NixlConnector", "kv_role": "kv_producer"}


def test_connector_to_kv_transfer_json_multi():
"""Test _connector_to_kv_transfer_json wraps multiple connectors in PdConnector."""
result = json.loads(_connector_to_kv_transfer_json(["kvbm", "nixl"]))
result = json.loads(_connector_to_kv_transfer_json(["kvbm", "nixl"], "kv_consumer"))
assert result["kv_connector"] == "PdConnector"
assert result["kv_role"] == "kv_both"
nested = result["kv_connector_extra_config"]["connectors"]
nested_names = [c["kv_connector"] for c in nested]
assert "DynamoConnector" in nested_names
assert "NixlConnector" in nested_names
assert {c["kv_connector"]: c["kv_role"] for c in nested} == {
"DynamoConnector": "kv_both",
"NixlConnector": "kv_consumer",
}


# _uses_nixl_connector / _uses_dynamo_connector tests
Expand All @@ -326,7 +346,7 @@ def _make_engine_cfg(kv_connector=None, extra_config=None):
_PD_KVBM_NIXL = {
"connectors": [
{"kv_connector": "DynamoConnector", "kv_role": "kv_both"},
{"kv_connector": "NixlConnector", "kv_role": "kv_both"},
{"kv_connector": "NixlConnector", "kv_role": "kv_producer"},
]
}

Expand Down Expand Up @@ -666,7 +686,7 @@ def test_disaggregation_mode_prefill(mock_vllm_cli):
"--disaggregation-mode",
"prefill",
"--kv-transfer-config",
'{"kv_connector":"NixlConnector","kv_role":"kv_both"}',
'{"kv_connector":"NixlConnector","kv_role":"kv_producer"}',
)
config = parse_args()
assert config.disaggregation_mode == DisaggregationMode.PREFILL
Expand Down Expand Up @@ -2053,7 +2073,7 @@ def test_rejects_prefill_disagg(self, mock_vllm_cli):
"--disaggregation-mode",
"prefill",
"--kv-transfer-config",
'{"kv_connector":"NixlConnector","kv_role":"kv_both"}',
'{"kv_connector":"NixlConnector","kv_role":"kv_producer"}',
)
with pytest.raises(ValueError, match="--embedding-worker is only valid"):
parse_args()
Expand Down
4 changes: 2 additions & 2 deletions deploy/operator/internal/dynamo/backend_vllm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ func TestVLLMBackend_UpdateContainer(t *testing.T) {
Args: []string{
"--model", "test", tensorParallelSizeFlag, "8",
"--kv-transfer-config",
`{"kv_connector": "NixlConnector", "kv_role": "kv_both"}`,
`{"kv_connector": "NixlConnector", "kv_role": "kv_producer"}`,
},
},
containerGPUs: 4,
expectedArgs: []string{fmt.Sprintf(
`ray start --head --port=%s && python3 -m dynamo.vllm --model test %s 8 --kv-transfer-config "{\"kv_connector\": \"NixlConnector\", \"kv_role\": \"kv_both\"}" --distributed-executor-backend ray`,
`ray start --head --port=%s && python3 -m dynamo.vllm --model test %s 8 --kv-transfer-config "{\"kv_connector\": \"NixlConnector\", \"kv_role\": \"kv_producer\"}" --distributed-executor-backend ray`,
VLLMPort, tensorParallelSizeFlag,
)},
expectProbesRemoved: true,
Expand Down
2 changes: 1 addition & 1 deletion deploy/utils/tests/fixtures/v1alpha1/disagg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
2 changes: 1 addition & 1 deletion deploy/utils/tests/fixtures/v1alpha1/disagg_planner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
2 changes: 1 addition & 1 deletion deploy/utils/tests/fixtures/v1alpha1/disagg_router.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ spec:
- --disaggregation-mode
- prefill
- --kv-transfer-config
- '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
- '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
- --kv-events-config
- '{"publisher":"zmq","topic":"kv-events","endpoint":"tcp://*:20080","enable_kv_cache_events":true}'
4 changes: 2 additions & 2 deletions lib/sidecar/vllm/launch/disagg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ vllm-rs serve "$MODEL" \
-- \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}' \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}' \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &

Expand All @@ -116,7 +116,7 @@ vllm-rs serve "$MODEL" \
-- \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}' \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_producer"}' \
--kv-events-config "{\"publisher\":\"zmq\",\"topic\":\"kv-events\",\"endpoint\":\"tcp://*:${VLLM_PREFILL_KV_EVENT_PORT}\",\"enable_kv_cache_events\":true}" \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &
Expand Down
11 changes: 6 additions & 5 deletions lib/sidecar/vllm/launch/disagg_kv_router.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ if [[ -z "$GPU_MEM_ARGS" ]]; then
GPU_MEM_ARGS="--kv-cache-memory-bytes $DEFAULT_KV_CACHE_BYTES --gpu-memory-utilization 0.01"
fi

KV_TRANSFER_CONFIG='{"kv_connector":"NixlConnector","kv_role":"kv_both"}'
KV_PRODUCER_CONFIG='{"kv_connector":"NixlConnector","kv_role":"kv_producer"}'
KV_CONSUMER_CONFIG='{"kv_connector":"NixlConnector","kv_role":"kv_consumer"}'
KV_EVENTS_CONFIG_1="{\"publisher\":\"zmq\",\"topic\":\"kv-events\",\"endpoint\":\"tcp://*:${VLLM_PREFILL1_KV_EVENT_PORT}\",\"enable_kv_cache_events\":true}"
KV_EVENTS_CONFIG_2="{\"publisher\":\"zmq\",\"topic\":\"kv-events\",\"endpoint\":\"tcp://*:${VLLM_PREFILL2_KV_EVENT_PORT}\",\"enable_kv_cache_events\":true}"

Expand All @@ -135,7 +136,7 @@ vllm-rs serve "$MODEL" \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--block-size "$VLLM_BLOCK_SIZE" \
--kv-transfer-config "$KV_TRANSFER_CONFIG" \
--kv-transfer-config "$KV_CONSUMER_CONFIG" \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &

Expand All @@ -151,7 +152,7 @@ vllm-rs serve "$MODEL" \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--block-size "$VLLM_BLOCK_SIZE" \
--kv-transfer-config "$KV_TRANSFER_CONFIG" \
--kv-transfer-config "$KV_CONSUMER_CONFIG" \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &

Expand All @@ -167,7 +168,7 @@ vllm-rs serve "$MODEL" \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--block-size "$VLLM_BLOCK_SIZE" \
--kv-transfer-config "$KV_TRANSFER_CONFIG" \
--kv-transfer-config "$KV_PRODUCER_CONFIG" \
--kv-events-config "$KV_EVENTS_CONFIG_1" \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &
Expand All @@ -184,7 +185,7 @@ vllm-rs serve "$MODEL" \
--enforce-eager \
--max-num-seqs "$MAX_CONCURRENT_SEQS" \
--block-size "$VLLM_BLOCK_SIZE" \
--kv-transfer-config "$KV_TRANSFER_CONFIG" \
--kv-transfer-config "$KV_PRODUCER_CONFIG" \
--kv-events-config "$KV_EVENTS_CONFIG_2" \
$GPU_MEM_ARGS \
"${EXTRA_ARGS[@]}" &
Expand Down
Loading
Loading