Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
20b1aee
feat: add decode context parallel (DCP) for Qwen3.5 GQA dense.
Enguikong Aug 5, 2026
45275cb
feat: add decode context parallel (DCP) for Qwen3.5 GQA dense.
Enguikong Aug 5, 2026
44cfe3d
refactor: extract DCP attention math into testable helper
Enguikong Aug 5, 2026
185f39d
feat: enable prefix cache for DCP
Enguikong Aug 5, 2026
def4450
feat: support DCP-aware chunked prefill
Enguikong Aug 6, 2026
15e5217
merge: reconcile fork DCP branch history
Enguikong Aug 6, 2026
1533da6
fix: close Phase 4 DCP validation gaps
Enguikong Aug 10, 2026
c80271c
fix: fail closed DCP chunked prefill
Enguikong Aug 10, 2026
7bac0b3
fix: close mixed batch when decode context parallel size > 1
Aug 10, 2026
0de5541
feat: gate DCP chunked prefill behind an experimental opt-in
Aug 10, 2026
d7f86fe
fix: gate DCP chunked prefill on effective, not raw, chunked flag
Aug 10, 2026
9f144b1
fix: use effective chunked prefill in whole-service DCP consumers
Aug 10, 2026
b319f2c
feat: allow Qwen3.5 MoE with decode context parallelism
Aug 10, 2026
05a8151
feat: warn on Qwen3.5 MoE with decode context parallelism
Aug 10, 2026
04aee08
feat: allow schedule overlap with decode context parallelism
Aug 13, 2026
3844bd5
feat: support decode context parallelism with ACL graph
Enguikong Aug 17, 2026
ab6003e
Merge upstream/main into feat/qwen35-dcp-correctness
Enguikong Aug 20, 2026
ffdb8bd
style: format DCP attention declaration
Enguikong Aug 20, 2026
4f64c0a
Merge upstream/main into feat/qwen35-dcp-correctness
Enguikong Aug 20, 2026
e5efe19
Merge remote-tracking branch 'upstream/main' into feat/qwen35-dcp-cor…
Enguikong Aug 20, 2026
a7ce44e
chore: drop unused NO_NPU_RUNTIME cc_test option
Enguikong Aug 20, 2026
ed74e9b
chore: prune out-of-scope changes flagged in DCP PR review
Enguikong Aug 24, 2026
95dda04
fix: scope DCP graph pre-replay barrier to FIA graph tasks
Enguikong Aug 24, 2026
805d9c4
Merge upstream/main into feat/qwen35-dcp-correctness
Enguikong Aug 24, 2026
eed08a5
Merge remote-tracking branch 'upstream/main' into feat/qwen35-dcp-cor…
Enguikong Aug 25, 2026
7ee62fc
fix: align GLM5.2 expert loading with lazy allocation
Enguikong Aug 25, 2026
e201280
fix: register Python bridge modules after interpreter init
Enguikong Aug 25, 2026
93830fd
ci: select NPU with visible device mask
Enguikong Aug 25, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/build_x86_64_npu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,4 @@ jobs:
timeout-minutes: 60
run: |
chmod +x ./cibuild/build_npu.sh
bash cibuild/build_npu.sh 'pip install pre-commit -i https://pypi.tuna.tsinghua.edu.cn/simple; python setup.py bdist_wheel; pip install dist/* --force-reinstall; cd /tmp && python /export/home/actions-runner/_work/xllm/xllm/examples/generate.py --model="/export/home/models/Qwen2-7B-Instruct" --devices="npu:7"'
bash cibuild/build_npu.sh 'pip install pre-commit -i https://pypi.tuna.tsinghua.edu.cn/simple; python setup.py bdist_wheel; pip install dist/* --force-reinstall; cd /tmp && ASCEND_RT_VISIBLE_DEVICES=7 python /export/home/actions-runner/_work/xllm/xllm/examples/generate.py --model="/export/home/models/Qwen2-7B-Instruct"'
9 changes: 9 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ if(USE_NPU)
)
endif()

add_test(
NAME xllm_export_import_test
COMMAND
"${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/python/import_xllm_export.py"
"$<TARGET_FILE:xllm_export>"
)
add_dependencies(all_tests xllm_export)

add_subdirectory(api_service)
add_subdirectory(core)
add_subdirectory(function_call)
Expand Down
3 changes: 3 additions & 0 deletions tests/core/common/options_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ TEST(OptionsTest, ContextParallelDefaultsToOneAcrossPublicApis) {
const XLLM_InitLLMOptions cc_options;

EXPECT_EQ(options.cp_size(), 1);
EXPECT_EQ(options.decode_context_parallel_size(), 1);
EXPECT_EQ(cc_options.cp_size, 1);
EXPECT_EQ(XLLM_INIT_LLM_OPTIONS_DEFAULT.cp_size, 1U);
EXPECT_EQ(XLLM_C_ABI_VERSION_MAJOR, 1);
Expand All @@ -40,12 +41,14 @@ TEST(OptionsTest, ContextParallelDefaultsToOneAcrossPublicApis) {
TEST(OptionsTest, ContextParallelAcceptsExplicitValuesAcrossPublicApis) {
Options options;
options.cp_size(4);
options.decode_context_parallel_size(2);
XLLM_InitLLMOptions cc_options;
cc_options.cp_size = 4;
XLLM_InitOptions c_options = XLLM_INIT_LLM_OPTIONS_DEFAULT;
c_options.cp_size = 4;

EXPECT_EQ(options.cp_size(), 4);
EXPECT_EQ(options.decode_context_parallel_size(), 2);
EXPECT_EQ(cc_options.cp_size, 4);
EXPECT_EQ(c_options.cp_size, 4U);
}
Expand Down
10 changes: 10 additions & 0 deletions tests/core/distributed_runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ cc_test(
DEPS
GTest::gtest_main
)

cc_test(
NAME
dcp_compat_test
SRCS
dcp_compat_test.cpp
DEPS
common
GTest::gtest_main
)
219 changes: 219 additions & 0 deletions tests/core/distributed_runtime/dcp_compat_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://github.com/jd-opensource/xllm/blob/main/LICENSE

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "core/distributed_runtime/dcp_compat.h"

#include <gtest/gtest.h>

#include <optional>
#include <string>

namespace xllm {
namespace {

Options dcp_options_with_supported_feature_flags() {
Options options;
options.decode_context_parallel_size(2)
.enable_chunked_prefill(false)
.enable_experimental_dcp_chunked_prefill(false)
.enable_prefix_cache(false)
.enable_schedule_overlap(false)
.enable_disagg_pd(false)
.instance_role(InstanceRole::DEFAULT)
.num_speculative_tokens(0);
return options;
}

Options dcp_options_with_experimental_chunked_prefill() {
Options options = dcp_options_with_supported_feature_flags();
options.enable_chunked_prefill(true).enable_experimental_dcp_chunked_prefill(
true);
return options;
}

void expect_error_contains(const std::optional<std::string>& error,
const std::string& expected) {
ASSERT_TRUE(error.has_value());
EXPECT_NE(error->find(expected), std::string::npos) << error.value();
}

TEST(DcpCompatTest, DcpOneDoesNotRejectDefaultOptions) {
Options options;
options.decode_context_parallel_size(1);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpOneExperimentalFlagHasNoEffect) {
Options options;
options.decode_context_parallel_size(1)
.enable_chunked_prefill(true)
.enable_experimental_dcp_chunked_prefill(true)
.priority_strategy("multi_slo_and_prio");

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpChunkedPrefillFalseWithExperimentalMatchesBaseline) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_experimental_dcp_chunked_prefill(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpMultiSloWithoutExperimentalRejected) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_chunked_prefill(false).priority_strategy("multi_slo_and_prio");

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"enable_experimental_dcp_chunked_prefill=true");
}

TEST(DcpCompatTest, DcpMultiSloWithExperimentalAllowed) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_chunked_prefill(false)
.priority_strategy("multi_slo_and_prio")
.enable_experimental_dcp_chunked_prefill(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, AllowsSupportedFirstVersionFeatureFlags) {
const Options options = dcp_options_with_supported_feature_flags();

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpOneChunkedPrefillWithoutExperimentalAllowed) {
Options options = dcp_options_with_supported_feature_flags();
options.decode_context_parallel_size(1).enable_chunked_prefill(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpChunkedPrefillWithoutExperimentalRejected) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_chunked_prefill(true);

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"enable_experimental_dcp_chunked_prefill=true");
}

TEST(DcpCompatTest, DcpChunkedPrefillWithExperimentalAllowed) {
const Options options = dcp_options_with_experimental_chunked_prefill();

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, DcpChunkedPrefillWithPrefixAndExperimentalAllowed) {
Options options = dcp_options_with_experimental_chunked_prefill();
options.enable_prefix_cache(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, AllowsPrefixCache) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_prefix_cache(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, AllowsScheduleOverlap) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_schedule_overlap(true);

EXPECT_FALSE(
validate_dcp_first_version_options(options, EngineType::LLM).has_value());
}

TEST(DcpCompatTest, RejectsDisaggregatedPrefillDecodeFlag) {
Options options = dcp_options_with_supported_feature_flags();
options.enable_disagg_pd(true);

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"enable_disagg_pd=false");
}

TEST(DcpCompatTest, RejectsDisaggregatedPrefillDecodeRole) {
Options options = dcp_options_with_supported_feature_flags();
options.instance_role(InstanceRole::DECODE);

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"instance_role=DEFAULT");
}

TEST(DcpCompatTest, RejectsSpeculativeEngineType) {
const Options options = dcp_options_with_supported_feature_flags();

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::SSM),
"speculative decoding");
}

TEST(DcpCompatTest, RejectsDraftModelPath) {
Options options = dcp_options_with_supported_feature_flags();
options.draft_model_path("/tmp/draft-model");

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"draft_model");
}

TEST(DcpCompatTest, RejectsSpeculativeTokens) {
Options options = dcp_options_with_supported_feature_flags();
options.num_speculative_tokens(1);

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"num_speculative_tokens=0");
}

// Enabling the experimental chunked prefill opt-in must not bypass the other
// first-version rejections: disaggregated PD and speculative decoding are still
// unsupported even under the experimental path. (Schedule overlap is now
// supported for eager DCP and is asserted by AllowsScheduleOverlap.)
TEST(DcpCompatTest, ExperimentalChunkedPrefillDoesNotBypassDisaggPd) {
Options options = dcp_options_with_experimental_chunked_prefill();
options.enable_disagg_pd(true);

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::LLM),
"enable_disagg_pd=false");
}

TEST(DcpCompatTest, ExperimentalChunkedPrefillDoesNotBypassSpeculative) {
const Options options = dcp_options_with_experimental_chunked_prefill();

expect_error_contains(
validate_dcp_first_version_options(options, EngineType::SSM),
"speculative decoding");
}

} // namespace
} // namespace xllm
5 changes: 5 additions & 0 deletions tests/core/distributed_runtime/spawn_worker_protocol_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,10 @@ TEST(SpawnWorkerProtocolTest, PreservesExplicitEmptyDtype) {
EXPECT_TRUE(indexer_cache_dtype->empty());
}

TEST(SpawnWorkerProtocolTest, AppendsDecodeContextParallelSizeAtTail) {
EXPECT_EQ(kDraftSamplingModeArgumentIndex, kArgumentCount - 2);
EXPECT_EQ(kDecodeContextParallelSizeArgumentIndex, kArgumentCount - 1);
}

} // namespace
} // namespace xllm::spawn_worker_protocol
51 changes: 46 additions & 5 deletions tests/core/framework/config/config_json_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,17 @@ class DumpConfigJsonFlagGuard final {

class CpSizeFlagGuard final {
public:
CpSizeFlagGuard() : old_cp_size_(FLAGS_cp_size) {}
~CpSizeFlagGuard() { FLAGS_cp_size = old_cp_size_; }
CpSizeFlagGuard()
: old_cp_size_(FLAGS_cp_size),
old_decode_context_parallel_size_(FLAGS_decode_context_parallel_size) {}
~CpSizeFlagGuard() {
FLAGS_cp_size = old_cp_size_;
FLAGS_decode_context_parallel_size = old_decode_context_parallel_size_;
}

private:
int32_t old_cp_size_;
int32_t old_decode_context_parallel_size_;
};

class ConfigFlagGuard final {
Expand Down Expand Up @@ -322,24 +328,59 @@ TEST(KVCacheConfigValidationTest, RejectsUnsupportedIndexerCacheDtypes) {

TEST(ConfigJsonTest, ParallelConfigReadsContextParallelSize) {
CpSizeFlagGuard flag_guard;
const JsonReader json =
config::parse_json_string(R"json({"cp_size": 4})json");
const JsonReader json = config::parse_json_string(
R"json({"cp_size": 4, "decode_context_parallel_size": 2})json");
ParallelConfig parallel_config;
parallel_config.from_json(json);

EXPECT_EQ(parallel_config.cp_size(), 4);
EXPECT_EQ(parallel_config.decode_context_parallel_size(), 2);
}

TEST(ConfigJsonTest, RegistersOnlyContextParallelCommandLineOption) {
TEST(ConfigJsonTest, RegistersContextParallelCommandLineOptions) {
google::CommandLineFlagInfo flag_info;
EXPECT_TRUE(google::GetCommandLineFlagInfo("cp_size", &flag_info));
EXPECT_EQ(flag_info.default_value, "1");
EXPECT_TRUE(google::GetCommandLineFlagInfo("decode_context_parallel_size",
&flag_info));
EXPECT_EQ(flag_info.default_value, "1");
EXPECT_TRUE(google::GetCommandLineFlagInfo(
"enable_experimental_dcp_chunked_prefill", &flag_info));
EXPECT_EQ(flag_info.default_value, "false");

const std::string removed_flag = std::string("enable_") + "prefill_sp";
EXPECT_FALSE(
google::GetCommandLineFlagInfo(removed_flag.c_str(), &flag_info));
}

TEST(ConfigJsonTest, ParallelConfigReadsExperimentalDcpChunkedPrefill) {
google::FlagSaver flag_saver;
const JsonReader json = config::parse_json_string(
R"json({"enable_experimental_dcp_chunked_prefill": true})json");
ParallelConfig parallel_config;
parallel_config.from_json(json);

EXPECT_TRUE(parallel_config.enable_experimental_dcp_chunked_prefill());
}

TEST(ConfigJsonTest, ParallelConfigDefaultsExperimentalDcpChunkedPrefillFalse) {
const ParallelConfig parallel_config;

EXPECT_FALSE(parallel_config.enable_experimental_dcp_chunked_prefill());
}

TEST(ConfigJsonTest, ParallelConfigDumpsExperimentalDcpChunkedPrefill) {
ParallelConfig parallel_config;
parallel_config.enable_experimental_dcp_chunked_prefill(true);

nlohmann::ordered_json config_json;
parallel_config.append_config_json(config_json);

ASSERT_TRUE(config_json.contains("enable_experimental_dcp_chunked_prefill"));
EXPECT_TRUE(
config_json.at("enable_experimental_dcp_chunked_prefill").get<bool>());
}

TEST(ConfigJsonTest, LoadJsonFileReadsConfigFixture) {
// The fixture sets more keys than ConfigFlagGuard restores, and from_json
// writes every resolved value back into its FLAGS_ global. FlagSaver reverts
Expand Down
3 changes: 3 additions & 0 deletions tests/core/framework/parallel_state/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ if(USE_NPU)
SRCS
cp_group_ranks_test.cpp
DEPS
block
dcp_attention_utils
parallel_state
torch
GTest::gtest_main
)

Expand Down
Loading
Loading