Files
project_6/upstream_ref/xllm/xllm/c_api/internal/rec.cpp
EX Engine 002f9879b2 ref(upstream): FULL TREE — Deep-Spark xllm (1470) + ds_vllm csrc/models (703)
Replaces cherry-picked upstream_ref with complete source trees.

xllm/ — Iluvatar official C++ inference engine (15MB, 1470 files)
  Complete: kernels → layers → models → runtime → scheduler → api
  Excluded: .git, binary images, third_party submodule checkouts

ds_vllm/ — Iluvatar official vllm fork (8MB, 703 files)
  Included: csrc/ (ALL CUDA kernels), fused_moe/, qwen3_5 model, _custom_ops
  Excluded: tests, benchmarks, docs, examples (not needed for reference)

Critical call chains now fully traceable:
  MoE: moe_topk_softmax_kernels.cuh → ixformer.h → fused_moe.cpp → layer
  GDN: qwen3_gated_delta_net_base.cpp → qwen3_5_gated_delta_net.cpp
  Attention: ixformer.h → xllm_paged_attention → attention.cpp
2026-08-10 02:54:03 +00:00

433 lines
15 KiB
C++

/* Copyright 2025 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 "c_api/rec.h"
#include <folly/Unit.h>
#include <folly/experimental/coro/Timeout.h>
#include <folly/futures/Future.h>
#include <glog/logging.h>
#include <pthread.h>
#include <atomic>
#include <cstring>
#include <exception>
#include <stdexcept>
#include "core/framework/model_loader.h"
#include "core/util/rec_model_utils.h"
#include "helper.h"
namespace {
const char* get_rec_pipeline_name(xllm::RecPipelineType pipeline_type) {
switch (pipeline_type) {
case xllm::RecPipelineType::kLlmRecDefault:
return "LlmRecEnginePipeline";
case xllm::RecPipelineType::kLlmRecWithMmData:
return "LlmRecWithMmData";
case xllm::RecPipelineType::kLlmRecMultiRoundPipeline:
return "RecMultiRoundEnginePipeline";
case xllm::RecPipelineType::kOneRecDefault:
return "OneRecPrefillOnlyEnginePipeline";
case xllm::RecPipelineType::kOneRecXAttentionPipeline:
return "OneRecXAttentionEnginePipeline";
default:
return "UnknownRecPipeline";
}
}
void reset_pipeline_runtime_toggles() {
FLAGS_enable_rec_fast_sampler = false;
FLAGS_enable_prefill_piecewise_graph = false;
FLAGS_enable_xattention_one_stage = false;
FLAGS_enable_graph_mode_decode_no_padding = false;
FLAGS_enable_rec_prefill_only = false;
FLAGS_enable_constrained_decoding = false;
FLAGS_enable_topk_sorted = false;
}
void apply_multi_round_pipeline_toggles() {
FLAGS_enable_rec_fast_sampler = true;
FLAGS_enable_prefill_piecewise_graph = true;
FLAGS_enable_xattention_one_stage = false;
FLAGS_enable_graph_mode_decode_no_padding = true;
FLAGS_enable_topk_sorted = false;
}
void apply_onerec_pipeline_toggles(xllm::Options* options) {
const bool enable_onerec_xattention = FLAGS_max_decode_rounds > 0;
FLAGS_enable_rec_prefill_only = !enable_onerec_xattention;
FLAGS_enable_constrained_decoding = true;
FLAGS_enable_prefix_cache = false;
FLAGS_enable_schedule_overlap = false;
FLAGS_enable_chunked_prefill = false;
options->enable_prefix_cache(false)
.enable_schedule_overlap(false)
.enable_chunked_prefill(false);
if (!enable_onerec_xattention) {
// Legacy OneRec keeps the historical fixed decode-step behavior.
FLAGS_max_decode_rounds = 0;
}
}
} // namespace
XLLM_CAPI_EXPORT XLLM_REC_Handler* xllm_rec_create(void) {
XLLM_REC_Handler* handler = new XLLM_REC_Handler();
CHECK(nullptr != handler);
handler->initialized = false;
handler->pipeline_type = xllm::RecPipelineType::kLlmRecDefault;
return handler;
}
XLLM_CAPI_EXPORT void xllm_rec_destroy(XLLM_REC_Handler* handler) {
if (!handler) return;
handler->master.reset();
handler->executor.reset();
handler->model_ids.clear();
handler->pipeline_type = xllm::RecPipelineType::kLlmRecDefault;
handler->initialized = false;
delete handler;
}
XLLM_CAPI_EXPORT void xllm_rec_init_options_default(
XLLM_InitOptions* init_options) {
if (nullptr == init_options) return;
*init_options = XLLM_INIT_REC_OPTIONS_DEFAULT;
}
XLLM_CAPI_EXPORT bool xllm_rec_initialize(
XLLM_REC_Handler* handler,
const char* model_path,
const char* devices,
const XLLM_InitOptions* init_options) {
if (!handler || !model_path || !devices) return false;
try {
XLLM_InitOptions xllm_init_options;
xllm::helper::set_init_options(
xllm::helper::BackendType::REC, init_options, &xllm_init_options);
std::string log_dir(xllm_init_options.log_dir);
if (!log_dir.empty()) {
xllm::helper::init_log(xllm_init_options.log_dir);
}
if (!std::filesystem::exists(model_path)) {
LOG(ERROR) << "model path[" << model_path << "] does not exist";
return false;
}
xllm::Options options;
options.model_path(model_path)
.task_type(xllm_init_options.task)
.devices(devices)
.draft_model_path(xllm_init_options.draft_model)
.draft_devices(xllm_init_options.draft_devices)
.backend("rec")
.block_size(xllm_init_options.block_size)
.max_cache_size(xllm_init_options.max_cache_size)
.max_memory_utilization(xllm_init_options.max_memory_utilization)
.enable_prefix_cache(xllm_init_options.enable_prefix_cache)
.max_tokens_per_batch(xllm_init_options.max_tokens_per_batch)
.max_seqs_per_batch(xllm_init_options.max_seqs_per_batch)
.max_tokens_per_chunk_for_prefill(
xllm_init_options.max_tokens_per_chunk_for_prefill)
.num_speculative_tokens(xllm_init_options.num_speculative_tokens)
.num_request_handling_threads(
xllm_init_options.num_request_handling_threads)
.communication_backend(xllm_init_options.communication_backend)
.expert_parallel_degree(xllm_init_options.expert_parallel_degree)
.enable_chunked_prefill(xllm_init_options.enable_chunked_prefill)
.master_node_addr(xllm_init_options.master_node_addr)
.device_ip(xllm_init_options.device_ip)
.transfer_listen_port(xllm_init_options.transfer_listen_port)
.nnodes(xllm_init_options.nnodes)
.node_rank(xllm_init_options.node_rank)
.dp_size(xllm_init_options.dp_size)
.ep_size(xllm_init_options.ep_size)
.instance_name(xllm_init_options.instance_name)
.enable_disagg_pd(xllm_init_options.enable_disagg_pd)
.enable_schedule_overlap(xllm_init_options.enable_schedule_overlap)
.enable_pd_ooc(xllm_init_options.enable_pd_ooc)
.kv_cache_transfer_mode(xllm_init_options.kv_cache_transfer_mode)
.enable_shm(xllm_init_options.enable_shm)
.is_local(true)
.server_idx(xllm_init_options.server_idx);
// @TODO: Currently, gflags are configured through hard coding, which needs
// to be improved in the future. For example, a separate gflags
// configuration file can be provided to the so for setting gflags.
//
// REC so still has two configuration paths:
// - some request/runtime code reads FLAGS_* directly
// - master/worker construction reads xllm::Options
//
// The fields copied from init options below are read from FLAGS_* today.
// beam_width/block_size/max_tokens/max_seqs are also represented in
// Options, so duplicated values must stay aligned.
FLAGS_beam_width = xllm_init_options.beam_width;
FLAGS_max_decode_rounds = xllm_init_options.max_decode_rounds;
FLAGS_max_seqs_per_batch = xllm_init_options.max_seqs_per_batch;
FLAGS_max_tokens_per_batch = xllm_init_options.max_tokens_per_batch;
FLAGS_block_size = xllm_init_options.block_size;
FLAGS_enable_rec_prefill_only = xllm_init_options.enable_rec_prefill_only;
FLAGS_enable_prefix_cache = xllm_init_options.enable_prefix_cache;
FLAGS_enable_schedule_overlap = xllm_init_options.enable_schedule_overlap;
FLAGS_enable_chunked_prefill = xllm_init_options.enable_chunked_prefill;
FLAGS_enable_graph = xllm_init_options.enable_graph;
FLAGS_rec_worker_max_concurrency =
xllm_init_options.rec_worker_max_concurrency;
FLAGS_enable_block_copy_kernel = xllm_init_options.enable_block_copy_kernel;
auto model_loader = xllm::ModelLoader::create(model_path);
if (model_loader == nullptr) {
LOG(ERROR) << "Failed to create model loader for path: " << model_path;
return false;
}
const auto& model_args = model_loader->model_args();
const xllm::RecModelKind rec_model_kind =
xllm::get_rec_model_kind(model_args.model_type());
if (rec_model_kind == xllm::RecModelKind::kNone) {
LOG(ERROR) << "Unsupported rec model_type: " << model_args.model_type();
return false;
}
const xllm::RecPipelineType pipeline_type =
xllm::get_rec_pipeline_type(rec_model_kind);
// Pipeline-specific runtime toggles in the REC so path.
reset_pipeline_runtime_toggles();
switch (pipeline_type) {
case xllm::RecPipelineType::kLlmRecMultiRoundPipeline:
apply_multi_round_pipeline_toggles();
break;
case xllm::RecPipelineType::kOneRecDefault:
case xllm::RecPipelineType::kOneRecXAttentionPipeline:
apply_onerec_pipeline_toggles(&options);
break;
case xllm::RecPipelineType::kLlmRecDefault:
case xllm::RecPipelineType::kLlmRecWithMmData:
break;
default:
LOG(ERROR) << "Unsupported rec pipeline type: "
<< static_cast<int32_t>(pipeline_type);
return false;
}
// Keep dual-source settings aligned with the FLAGS_* values above.
options.enable_graph(FLAGS_enable_graph)
.beam_width(FLAGS_beam_width)
.rec_worker_max_concurrency(FLAGS_rec_worker_max_concurrency);
LOG(INFO) << "REC C API selected pipeline="
<< get_rec_pipeline_name(pipeline_type)
<< ", model_type=" << model_args.model_type()
<< ", enable_rec_prefill_only=" << FLAGS_enable_rec_prefill_only
<< ", enable_constrained_decoding="
<< FLAGS_enable_constrained_decoding
<< ", enable_prefix_cache=" << FLAGS_enable_prefix_cache
<< ", enable_schedule_overlap=" << FLAGS_enable_schedule_overlap
<< ", enable_chunked_prefill=" << FLAGS_enable_chunked_prefill
<< ", enable_rec_fast_sampler=" << FLAGS_enable_rec_fast_sampler
<< ", max_decode_rounds=" << FLAGS_max_decode_rounds;
#if !defined(USE_NPU) && !defined(USE_CUDA)
FLAGS_enable_block_copy_kernel = false;
#endif
handler->master = std::make_unique<xllm::RecMaster>(options);
handler->master->run();
size_t cpu_cores = std::thread::hardware_concurrency();
size_t thread_num = std::clamp((cpu_cores == 0) ? 8 : cpu_cores / 2,
static_cast<size_t>(4),
static_cast<size_t>(16));
handler->executor =
std::make_unique<folly::CPUThreadPoolExecutor>(thread_num);
std::filesystem::path model_path_fs =
std::filesystem::path(model_path).lexically_normal();
std::string model_id;
if (model_path_fs.has_filename()) {
model_id = model_path_fs.filename().string();
} else if (!model_path_fs.empty()) {
model_id = model_path_fs.string();
} else {
model_id = "default";
}
handler->model_ids.clear();
handler->model_ids.emplace_back(model_id);
handler->pipeline_type = pipeline_type;
handler->initialized = true;
return true;
} catch (const std::exception& e) {
LOG(ERROR) << "LLM initialization failed: " << e.what();
}
handler->master.reset();
handler->executor.reset();
handler->model_ids.clear();
handler->pipeline_type = xllm::RecPipelineType::kLlmRecDefault;
handler->initialized = false;
return false;
}
XLLM_CAPI_EXPORT void xllm_rec_request_params_default(
XLLM_RequestParams* request_params) {
if (nullptr == request_params) return;
*request_params = XLLM_REC_REQUEST_PARAMS_DEFAULT;
}
XLLM_CAPI_EXPORT XLLM_Response* xllm_rec_text_completions(
XLLM_REC_Handler* handler,
const char* model_id,
const char* prompt,
uint32_t timeout_ms,
const XLLM_RequestParams* request_params) {
if (!handler || !model_id || *model_id == '\0' || !prompt ||
*prompt == '\0') {
return xllm::helper::build_error_response(
"", XLLM_StatusCode::kInvalidRequest, "Invalid input parameters");
}
return xllm::helper::handle_inference_request(
handler,
xllm::helper::InferenceType::REC_COMPLETIONS,
model_id,
prompt,
nullptr,
timeout_ms,
request_params);
}
XLLM_CAPI_EXPORT XLLM_Response* xllm_rec_token_completions(
XLLM_REC_Handler* handler,
const char* model_id,
const int32_t* token_ids,
size_t token_size,
uint32_t timeout_ms,
const XLLM_RequestParams* request_params) {
if (!handler || !model_id || *model_id == '\0' || !token_ids ||
token_size == 0) {
return xllm::helper::build_error_response(
"", XLLM_StatusCode::kInvalidRequest, "Invalid input parameters");
}
std::vector<int> token_ids_vec;
for (int i = 0; i < token_size; i++) {
token_ids_vec.push_back(token_ids[i]);
}
return xllm::helper::handle_inference_request(
handler,
xllm::helper::InferenceType::REC_COMPLETIONS,
model_id,
token_ids_vec,
nullptr,
timeout_ms,
request_params);
}
XLLM_CAPI_EXPORT XLLM_Response* xllm_rec_multimodal_completions(
XLLM_REC_Handler* handler,
const char* model_id,
const int32_t* token_ids,
size_t token_size,
const XLLM_MM_Data* mm_data,
uint32_t timeout_ms,
const XLLM_RequestParams* request_params) {
if (!handler || !model_id || *model_id == '\0' || !token_ids ||
token_size == 0) {
return xllm::helper::build_error_response(
"", XLLM_StatusCode::kInvalidRequest, "Invalid input parameters");
}
if (!mm_data) {
return xllm_rec_token_completions(
handler, model_id, token_ids, token_size, timeout_ms, request_params);
}
xllm::MMData internal_mm_data;
try {
bool ret = xllm::helper::convert_xllm_mm_data_to_internal(mm_data,
internal_mm_data);
if (!ret) {
return xllm::helper::build_error_response(
"", XLLM_StatusCode::kInternalError, "Fail in mm_data conversion");
}
} catch (const std::exception& e) {
return xllm::helper::build_error_response(
"",
XLLM_StatusCode::kInternalError,
"Critical error in mm_data conversion: " + std::string(e.what()));
}
std::vector<int> token_ids_vec;
for (int i = 0; i < token_size; i++) {
token_ids_vec.push_back(token_ids[i]);
}
return xllm::helper::handle_inference_request(
handler,
xllm::helper::InferenceType::REC_COMPLETIONS,
model_id,
token_ids_vec,
static_cast<void*>(&internal_mm_data),
timeout_ms,
request_params);
}
XLLM_CAPI_EXPORT XLLM_Response* xllm_rec_chat_completions(
XLLM_REC_Handler* handler,
const char* model_id,
const XLLM_ChatMessage* messages,
size_t messages_count,
uint32_t timeout_ms,
const XLLM_RequestParams* request_params) {
if (!handler || !model_id || *model_id == '\0' || !messages ||
messages_count == 0) {
return xllm::helper::build_error_response(
"", XLLM_StatusCode::kInvalidRequest, "Invalid input parameters");
}
std::vector<xllm::Message> xllm_messages;
xllm_messages.reserve(messages_count);
for (int i = 0; i < messages_count; i++) {
xllm_messages.emplace_back(messages[i].role, messages[i].content);
}
return xllm::helper::handle_inference_request(
handler,
xllm::helper::InferenceType::REC_CHAT_COMPLETIONS,
model_id,
xllm_messages,
nullptr,
timeout_ms,
request_params);
}
XLLM_CAPI_EXPORT void xllm_rec_free_response(XLLM_Response* resp) {
return xllm::helper::xllm_free_response(resp);
}