From 90828cd007401e955955a1a0c78ad140caf8a9c6 Mon Sep 17 00:00:00 2001
From: "Zhang, Liangang"
Date: Thu, 20 Feb 2025 17:24:16 -0800
Subject: [PATCH 01/14] Add changes for xpu
---
ktransformers/local_chat.py | 3 +-
ktransformers/optimize/optimize.py | 5 +-
.../optimize_rules/DeepSeek-V2-Chat-XPU.yaml | 56 +++++++++++++++++++
3 files changed, 62 insertions(+), 2 deletions(-)
create mode 100644 ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
diff --git a/ktransformers/local_chat.py b/ktransformers/local_chat.py
index 676ea670..41e50762 100644
--- a/ktransformers/local_chat.py
+++ b/ktransformers/local_chat.py
@@ -61,6 +61,7 @@ def local_chat(
prompt_file : str | None = None,
mode: str = "normal",
force_think: bool = False,
+ device: str = "cuda",
):
@@ -171,7 +172,7 @@ def local_chat(
torch.bfloat16
) # TODO: Remove this, replace dtype using config
generated = prefill_and_generate(
- model, tokenizer, input_tensor.cuda(), max_new_tokens, use_cuda_graph, mode, force_think
+ model, tokenizer, input_tensor.to(device), max_new_tokens, use_cuda_graph, mode, force_think
)
diff --git a/ktransformers/optimize/optimize.py b/ktransformers/optimize/optimize.py
index 32eab017..6a19dce9 100644
--- a/ktransformers/optimize/optimize.py
+++ b/ktransformers/optimize/optimize.py
@@ -129,4 +129,7 @@ def optimize_and_load_gguf(module: nn.Module, rule_file: str, gguf_path: str, mo
load_weights(module, gguf_loader)
module.gguf_loader = gguf_loader
del_meta(module)
- torch.cuda.empty_cache()
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ elif torch.xpu.is_available():
+ torch.xpu.empty_cache()
diff --git a/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml b/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
new file mode 100644
index 00000000..ef83654d
--- /dev/null
+++ b/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
@@ -0,0 +1,56 @@
+- match:
+ class: ktransformers.models.modeling_deepseek.DeepseekV2YarnRotaryEmbedding
+ replace:
+ class: ktransformers.operators.RoPE.YarnRotaryEmbedding
+ kwargs:
+ generate_device: "xpu"
+ prefill_device: "xpu"
+- match:
+ name: "^model\\.layers\\.(?!.*self_attn\\.kv_b_proj).*$" # regular expression
+ class: torch.nn.Linear # only match modules matching name and class simultaneously
+ replace:
+ class: ktransformers.operators.linear.KTransformersLinear # optimized Kernel on quantized data types
+ kwargs:
+ generate_device: "xpu"
+ prefill_device: "xpu"
+ generate_op: "KLinearMarlin"
+ prefill_op: "KLinearTorch"
+- match:
+ name: "^model\\.layers\\..*\\.mlp$"
+ class: ktransformers.models.modeling_deepseek.DeepseekV2MoE
+ replace:
+ class: ktransformers.operators.experts.KDeepseekV2MoE # mlp module with custom forward function
+ kwargs:
+ generate_device: "xpu"
+ prefill_device: "xpu"
+- match:
+ name: "^model\\.layers\\..*\\.mlp\\.experts$"
+ replace:
+ class: ktransformers.operators.experts.KTransformersExperts # custom MoE Kernel with expert paralleism
+ kwargs:
+ prefill_device: "xpu"
+ prefill_op: "KExpertsTorch"
+ generate_device: "cpu"
+ generate_op: "KExpertsCPU"
+ out_device: "xpu"
+ recursive: False # don't recursively inject submodules of this module
+- match:
+ name: "^model\\.layers\\..*\\.self_attn$"
+ replace:
+ class: ktransformers.operators.attention.KDeepseekV2Attention # optimized MLA implementation
+ kwargs:
+ generate_device: "xpu"
+ prefill_device: "xpu"
+- match:
+ name: "^model$"
+ replace:
+ class: "ktransformers.operators.models.KDeepseekV2Model"
+ kwargs:
+ per_layer_prefill_intput_threshold: 0 # 0 is close layer wise prefill
+- match:
+ name: "^model.embed_tokens"
+ replace:
+ class: "default"
+ kwargs:
+ generate_device: "cpu"
+ prefill_device: "cpu"
\ No newline at end of file
From 809ef09996c24205996d7660c50f0f3fd949fc17 Mon Sep 17 00:00:00 2001
From: root
Date: Tue, 25 Feb 2025 06:23:38 +0000
Subject: [PATCH 02/14] Enable xpu path
---
.../ktransformers_ext/CMakeLists.txt | 29 ++++++++---------
.../ktransformers_ext/cpu_backend/cpuinfer.h | 23 ++++++++++----
.../ktransformers_ext/ext_bindings.cpp | 4 ++-
ktransformers/local_chat.py | 8 +++--
ktransformers/operators/dynamic_attention.py | 4 ++-
ktransformers/operators/experts.py | 4 +--
ktransformers/operators/linear.py | 19 +++++++-----
ktransformers/optimize/optimize.py | 11 +++++--
.../optimize_rules/DeepSeek-V2-Chat-XPU.yaml | 2 +-
ktransformers/util/custom_gguf.py | 6 ++--
ktransformers/util/utils.py | 31 +++++++++++++++----
setup.py | 21 +++++++++----
12 files changed, 112 insertions(+), 50 deletions(-)
diff --git a/ktransformers/ktransformers_ext/CMakeLists.txt b/ktransformers/ktransformers_ext/CMakeLists.txt
index d9ecd7a7..51f191bc 100644
--- a/ktransformers/ktransformers_ext/CMakeLists.txt
+++ b/ktransformers/ktransformers_ext/CMakeLists.txt
@@ -205,12 +205,13 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/pybind11 ${CMAKE_
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/llama.cpp ${CMAKE_CURRENT_BINARY_DIR}/third_party/llama.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party)
-if (WIN32)
- include_directories("$ENV{CUDA_PATH}/include")
-elseif (UNIX)
- find_package(CUDA REQUIRED)
- include_directories("${CUDA_INCLUDE_DIRS}")
-endif()
+
+# if (WIN32)
+# include_directories("$ENV{CUDA_PATH}/include")
+# elseif (UNIX)
+# find_package(CUDA REQUIRED)
+# include_directories("${CUDA_INCLUDE_DIRS}")
+# endif()
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_DIR1)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/cpu_backend SOURCE_DIR2)
@@ -222,14 +223,14 @@ message(STATUS "ALL_SOURCES: ${ALL_SOURCES}")
pybind11_add_module(${PROJECT_NAME} MODULE ${ALL_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE llama)
-if(WIN32)
- target_link_libraries(${PROJECT_NAME} PRIVATE "$ENV{CUDA_PATH}/lib/x64/cudart.lib")#CUDA::cudart
-elseif(UNIX)
- if(NOT DEFINED ENV{CUDA_HOME} OR "$ENV{CUDA_HOME}" STREQUAL "")
- set(ENV{CUDA_HOME} "/usr/local/cuda")
- endif()
- target_link_libraries(${PROJECT_NAME} PRIVATE "$ENV{CUDA_HOME}/lib64/libcudart.so")
-endif()
+# if(WIN32)
+# target_link_libraries(${PROJECT_NAME} PRIVATE "$ENV{CUDA_PATH}/lib/x64/cudart.lib")#CUDA::cudart
+# elseif(UNIX)
+# if(NOT DEFINED ENV{CUDA_HOME} OR "$ENV{CUDA_HOME}" STREQUAL "")
+# set(ENV{CUDA_HOME} "/usr/local/cuda")
+# endif()
+# target_link_libraries(${PROJECT_NAME} PRIVATE "$ENV{CUDA_HOME}/lib64/libcudart.so")
+# endif()
# Define the USE_NUMA option
option(USE_NUMA "Disable NUMA support" OFF)
diff --git a/ktransformers/ktransformers_ext/cpu_backend/cpuinfer.h b/ktransformers/ktransformers_ext/cpu_backend/cpuinfer.h
index 9618e6b6..8af6cf4d 100644
--- a/ktransformers/ktransformers_ext/cpu_backend/cpuinfer.h
+++ b/ktransformers/ktransformers_ext/cpu_backend/cpuinfer.h
@@ -17,7 +17,10 @@
#include
#include
#include
-#include "cuda_runtime.h"
+//only include for CUDA backend
+#ifdef __CUDA_ARCH__
+ #include "cuda_runtime.h"
+#endif
#include "backend.h"
#include "task_queue.h"
@@ -58,10 +61,14 @@ class CPUInfer {
}
void submit_with_cuda_stream(intptr_t user_cuda_stream, std::pair params) {
- void (*func)(void*) = (void (*)(void*))params.first;
- void* args = (void*)params.second;
- *((CPUInfer**)args) = this;
- cudaLaunchHostFunc((cudaStream_t)user_cuda_stream, (cudaHostFn_t)func, args);
+ #ifdef __CUDA_ARCH__
+ void (*func)(void*) = (void (*)(void*))params.first;
+ void* args = (void*)params.second;
+ *((CPUInfer**)args) = this;
+ cudaLaunchHostFunc((cudaStream_t)user_cuda_stream, (cudaHostFn_t)func, args);
+ #else
+ submit(params);
+ #endif
}
static void sync_(void* cpu_infer_ptr) {
@@ -70,7 +77,11 @@ class CPUInfer {
}
void sync_with_cuda_stream(intptr_t user_cuda_stream) {
- cudaLaunchHostFunc((cudaStream_t)user_cuda_stream, (cudaHostFn_t)&sync_, (void*)this);
+ #ifdef __CUDA_ARCH__
+ cudaLaunchHostFunc((cudaStream_t)user_cuda_stream, (cudaHostFn_t)&sync_, (void*)this);
+ #else
+ sync();
+ #endif
}
public:
diff --git a/ktransformers/ktransformers_ext/ext_bindings.cpp b/ktransformers/ktransformers_ext/ext_bindings.cpp
index 902d4271..7d3b6b20 100644
--- a/ktransformers/ktransformers_ext/ext_bindings.cpp
+++ b/ktransformers/ktransformers_ext/ext_bindings.cpp
@@ -9,7 +9,9 @@
**/
// Python bindings
#include "cpu_backend/cpuinfer.h"
-#include "device_launch_parameters.h"
+#ifdef __CUDA_ARCH__
+ #include "device_launch_parameters.h"
+#endif
#include "llamafile/flags.h"
#include "operators/kvcache/kvcache.h"
#include "operators/llamafile/linear.h"
diff --git a/ktransformers/local_chat.py b/ktransformers/local_chat.py
index 41e50762..91ec0078 100644
--- a/ktransformers/local_chat.py
+++ b/ktransformers/local_chat.py
@@ -68,6 +68,10 @@ def local_chat(
torch.set_grad_enabled(False)
Config().cpu_infer = cpu_infer
+
+ if device != "cuda":
+ Warning("cuda graph is only supported on cuda device, please set use_cuda_graph to False")
+ use_cuda_graph = False
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
config = AutoConfig.from_pretrained(model_path, trust_remote_code=True)
@@ -108,7 +112,7 @@ def local_chat(
gguf_path = input(
"please input the path of your gguf file(gguf file in the dir containing input gguf file must all belong to current model):"
)
- optimize_and_load_gguf(model, optimize_rule_path, gguf_path, config)
+ optimize_and_load_gguf(model, optimize_rule_path, gguf_path, config, default_device=device)
try:
model.generation_config = GenerationConfig.from_pretrained(model_path)
@@ -177,4 +181,4 @@ def local_chat(
if __name__ == "__main__":
- fire.Fire(local_chat)
\ No newline at end of file
+ fire.Fire(local_chat)
diff --git a/ktransformers/operators/dynamic_attention.py b/ktransformers/operators/dynamic_attention.py
index 13a74b43..051744bf 100644
--- a/ktransformers/operators/dynamic_attention.py
+++ b/ktransformers/operators/dynamic_attention.py
@@ -17,7 +17,9 @@
logger = logging.getLogger("dynamic_attention")
sys.path.append(os.path.dirname(__file__) + "/../ktransformers_ext/cpu_backend")
from ktransformers.operators.cpuinfer import CPUInfer, CPUInferKVCache
-from flash_attn import flash_attn_func, flash_attn_with_kvcache
+
+if torch.cuda.is_available():
+ from flash_attn import flash_attn_func, flash_attn_with_kvcache
import math
diff --git a/ktransformers/operators/experts.py b/ktransformers/operators/experts.py
index 274a3cad..89cf2c22 100644
--- a/ktransformers/operators/experts.py
+++ b/ktransformers/operators/experts.py
@@ -202,7 +202,7 @@ def sync_for_one_decode(self):
def forward(self, input_tensor, expert_ids, weights):
# generate, capture and run cuda graph
# print(expert_ids)
- if input_tensor.size(0)==1 and torch.cuda.is_current_stream_capturing():
+ if input_tensor.size(0)==1 and torch.cuda.is_available() and torch.cuda.is_current_stream_capturing():
# TODO: this branch is unreachable, but the shape of input_tensor([1,hidden_size]) and input_tensor_cpu([hidden_size]) is not compatible
#print("capturing experts")
KExpertsCPU.input_tensor_cpu.copy_(input_tensor, non_blocking=True)
@@ -659,7 +659,7 @@ def forward(self, hidden_states):
topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
- if sequence_length == 1 and hasattr(self.experts.generate_experts, "submit_for_one_decode") and torch.cuda.is_current_stream_capturing():
+ if sequence_length == 1 and torch.cuda.is_available() and hasattr(self.experts.generate_experts, "submit_for_one_decode") and torch.cuda.is_current_stream_capturing():
self.experts.generate_experts.submit_for_one_decode(hidden_states[0], topk_idx[0], topk_weight[0])
if self.config.n_shared_experts is not None:
y_ = self.shared_experts(identity).squeeze(0)
diff --git a/ktransformers/operators/linear.py b/ktransformers/operators/linear.py
index 9e35e8d7..aad05d02 100644
--- a/ktransformers/operators/linear.py
+++ b/ktransformers/operators/linear.py
@@ -14,15 +14,20 @@
import ctypes
import torch
from torch import Tensor, nn
-import KTransformersOps
+
+if torch.cuda.is_available():
+ import KTransformersOps
+
from ktransformers.util.custom_gguf import GGUFLoader
from ktransformers.util.utils import InferenceState
-from ktransformers.ktransformers_ext.operators.custom_marlin.quantize.utils.marlin_utils import (
- MarlinWorkspace,
- marlin_quantize,
- GPTQ_MARLIN_MIN_THREAD_N,
- GPTQ_MARLIN_MAX_PARALLEL,
-)
+
+if torch.cuda.is_available():
+ from ktransformers.ktransformers_ext.operators.custom_marlin.quantize.utils.marlin_utils import (
+ MarlinWorkspace,
+ marlin_quantize,
+ GPTQ_MARLIN_MIN_THREAD_N,
+ GPTQ_MARLIN_MAX_PARALLEL,
+ )
from ktransformers.operators.base_operator import BaseInjectedModule
from transformers.configuration_utils import PretrainedConfig
from abc import ABC, abstractmethod
diff --git a/ktransformers/optimize/optimize.py b/ktransformers/optimize/optimize.py
index 6a19dce9..b71da6a3 100644
--- a/ktransformers/optimize/optimize.py
+++ b/ktransformers/optimize/optimize.py
@@ -76,10 +76,12 @@ def gen_optimize_config(module: nn.Module, out_data: Mapping, rule_list: List, p
if "replace" in rule:
replace_meta = rule["replace"]
if module_name not in out_data:
+
out_data[module_name]={"key": translated_name,
"class": replace_meta["class"] if "class" in replace_meta else "default",
# "device": replace_meta["device"] if "device" in replace_meta else default_device,
"kwargs": copy.deepcopy(replace_meta["kwargs"]) if "kwargs" in replace_meta else dict()}
+ print("after:", out_data[module_name])
else:
if out_data[module_name]["class"] == "default":
out_data[module_name]["class"] = replace_meta["class"] if "class" in replace_meta else "default"
@@ -96,14 +98,14 @@ def gen_optimize_config(module: nn.Module, out_data: Mapping, rule_list: List, p
"prefill_device": default_device}
}
- #print(out_data[module_name])
+ print(out_data[module_name])
#input()
if recursive:
for name, child in module._modules.items():
if child is not None:
child_prefix = prefix + name + "."
- gen_optimize_config(child, out_data, rule_list, child_prefix)
+ gen_optimize_config(child, out_data, rule_list, child_prefix, default_device = default_device)
def translate_model_config(model_config: PretrainedConfig):
@@ -115,10 +117,15 @@ def translate_model_config(model_config: PretrainedConfig):
def optimize_and_load_gguf(module: nn.Module, rule_file: str, gguf_path: str, model_config: PretrainedConfig, default_device: str = "cuda:0"):
+ if 'cuda' in default_device:
+ default_device = "cuda:0"
+ elif 'xpu' in default_device:
+ default_device = "xpu:0"
with open(rule_file, 'r', encoding='utf-8') as f:
rule_list = yaml.load(f.read(), Loader=yaml.FullLoader)
optimize_config = dict()
+ print("###########Default device###########", default_device)
gen_optimize_config(module, optimize_config, rule_list, default_device = default_device)
model_config = translate_model_config(model_config)
diff --git a/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml b/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
index ef83654d..f832b172 100644
--- a/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
+++ b/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
@@ -13,7 +13,7 @@
kwargs:
generate_device: "xpu"
prefill_device: "xpu"
- generate_op: "KLinearMarlin"
+ generate_op: "KLinearTorch"
prefill_op: "KLinearTorch"
- match:
name: "^model\\.layers\\..*\\.mlp$"
diff --git a/ktransformers/util/custom_gguf.py b/ktransformers/util/custom_gguf.py
index a90c0edb..60b586a1 100644
--- a/ktransformers/util/custom_gguf.py
+++ b/ktransformers/util/custom_gguf.py
@@ -24,7 +24,9 @@
import os
from enum import IntEnum
import torch
-import KTransformersOps
+
+if torch.cuda.is_available():
+ import KTransformersOps
class GGMLQuantizationType(IntEnum):
F32 = 0
@@ -296,7 +298,7 @@ def load_gguf_tensor(self, name: str, device:str = "cpu")->torch.Tensor:
#values = torch.from_numpy(values).to(device = device)
else:
values = GGML_DEQUANTIZE[ggml_name](data)
- values = torch.from_numpy(values)
+ values = torch.from_numpy(values).to(device)
values = values.view(shape[::-1])
if "attn_q" in name and self.gguf_file_meta['general.architecture'] in ["llama"]:
n_head = self.gguf_file_meta['llama.attention.head_count']
diff --git a/ktransformers/util/utils.py b/ktransformers/util/utils.py
index 88c33fd6..5fb0e6bd 100644
--- a/ktransformers/util/utils.py
+++ b/ktransformers/util/utils.py
@@ -84,6 +84,17 @@ def load_weights(module:nn.Module, gguf_loader:GGUFLoader, prefix=''):
else:
module.load()
+def sync_all_device(all_device_list):
+ for device in all_device_list:
+ if "cuda" in device.lower():
+ torch.cuda.synchronize(device)
+ elif "xpu" in device.lower():
+ torch.xpu.synchronize(device)
+ else:
+ raise RuntimeError("The device {} is not available".format(device))
+
+torch_device_mapping ={"cuda": "cuda:0", "xpu": "xpu:0"}
+
def prefill_and_generate(model, tokenizer, inputs, max_new_tokens=10000, use_cuda_graph: bool = True,
mode = 'normal', force_think: bool = False):
import os
@@ -92,7 +103,7 @@ def prefill_and_generate(model, tokenizer, inputs, max_new_tokens=10000, use_cud
batch_size, seq_length = inputs.shape
device_map = model.gguf_loader.tensor_device_map
torch_device = get_device('blk.0.self_attn', device_map)
- torch_device = "cuda:0" if torch_device == "cuda" else torch_device
+ torch_device = torch_device_mapping[torch_device] if torch_device in torch_device_mapping else torch_device
inputs = inputs.to(torch_device)
all_cuda_device = get_all_used_cuda_device(device_map)
@@ -103,7 +114,12 @@ def decode_one_tokens(cuda_graph_runner, cur_token, position_ids, cache_position
logits = cuda_graph_runner(cur_token, position_ids, cache_position)
else:
# custom_stream = torch.cuda.Stream()
- torch.cuda.set_device(torch_device)
+ if torch.cuda.is_available():
+ torch.cuda.set_device(torch_device)
+ elif torch.xpu.is_available():
+ torch.xpu.set_device(torch_device)
+ else:
+ RuntimeError("The device: {torch_device} is not available")
inputs_embeds = model.model.embed_tokens(cur_token.to("cpu")).to(torch_device)
# with torch.cuda.stream(custom_stream):
logits=model(inputs_embeds=inputs_embeds,
@@ -113,8 +129,7 @@ def decode_one_tokens(cuda_graph_runner, cur_token, position_ids, cache_position
return_dict=False, use_cache=True)[0]
if past_key_values != None:
past_key_values.change_seq_length(1)
- for device in all_cuda_device:
- torch.cuda.synchronize(device)
+ sync_all_device(all_cuda_device)
#print(logits)
next_token_scores = logits_warper(inputs, logits[:, -1, :])
if generation_config.do_sample:
@@ -123,8 +138,12 @@ def decode_one_tokens(cuda_graph_runner, cur_token, position_ids, cache_position
else:
next_token = torch.argmax(next_token_scores, dim=-1)
return next_token
-
- torch.cuda.set_device(torch_device)
+ if torch.cuda.is_available():
+ torch.cuda.set_device(torch_device)
+ elif torch.xpu.is_available():
+ torch.xpu.set_device(torch_device)
+ else:
+ RuntimeError("The device is not available")
with torch.no_grad():
stream = TextStreamer(tokenizer)
if mode != 'long_context':
diff --git a/setup.py b/setup.py
index d24db14b..7aa9380b 100644
--- a/setup.py
+++ b/setup.py
@@ -129,7 +129,10 @@ def get_flash_version(self,):
def get_package_version(self, full_version=False):
flash_version = self.get_flash_version()
- package_version = f"{str(flash_version)}+cu{self.get_cuda_bare_metal_version(CUDA_HOME)}torch{self.get_torch_version()}{self.get_cpu_instruct()}"
+ if torch.cuda.is_available():
+ package_version = f"{str(flash_version)}+cu{self.get_cuda_bare_metal_version(CUDA_HOME)}torch{self.get_torch_version()}{self.get_cpu_instruct()}"
+ elif torch.xpu.is_available():
+ package_version = f"{str(flash_version)}+xpu{self.get_torch_version()}{self.get_cpu_instruct()}"
if full_version:
return package_version
if not VersionInfo.FORCE_BUILD:
@@ -291,17 +294,14 @@ def build_extension(self, ext) -> None:
["cmake", "--build", ".", *build_args], cwd=build_temp, check=True
)
-
-setup(
- version=VersionInfo().get_package_version(),
- cmdclass={"bdist_wheel":BuildWheelsCommand ,"build_ext": CMakeBuild},
+if torch.cuda.is_available():
ext_modules=[
CMakeExtension("cpuinfer_ext"),
CUDAExtension('KTransformersOps', [
'ktransformers/ktransformers_ext/cuda/custom_gguf/dequant.cu',
'ktransformers/ktransformers_ext/cuda/binding.cpp',
'ktransformers/ktransformers_ext/cuda/gptq_marlin/gptq_marlin.cu'
- ],
+ ] ,
extra_compile_args={
'cxx': ['-O3'],
'nvcc': [
@@ -312,4 +312,13 @@ def build_extension(self, ext) -> None:
}
)
]
+elif torch.xpu.is_available():
+ ext_modules=[
+ CMakeExtension("cpuinfer_ext"),
+ ]
+
+setup(
+ version=VersionInfo().get_package_version(),
+ cmdclass={"bdist_wheel":BuildWheelsCommand ,"build_ext": CMakeBuild},
+ ext_modules=ext_modules,
)
From 48d7220f91c4393356b78db845c0a0bdab9e2390 Mon Sep 17 00:00:00 2001
From: root
Date: Tue, 25 Feb 2025 08:30:45 +0000
Subject: [PATCH 03/14] Fix rebase issue
---
ktransformers/local_chat.py | 2 +-
ktransformers/operators/attention.py | 9 ++++---
ktransformers/operators/linear.py | 15 ++++++-----
ktransformers/optimize/optimize.py | 5 +---
ktransformers/util/custom_gguf.py | 3 ++-
setup.py | 40 +++++++++++++++++++---------
6 files changed, 44 insertions(+), 30 deletions(-)
diff --git a/ktransformers/local_chat.py b/ktransformers/local_chat.py
index 47a68911..44a01c40 100644
--- a/ktransformers/local_chat.py
+++ b/ktransformers/local_chat.py
@@ -180,7 +180,7 @@ def local_chat(
)
else:
generated = prefill_and_generate(
- model, tokenizer, input_tensor.cuda(), max_new_tokens, use_cuda_graph, mode = mode, force_think = force_think,
+ model, tokenizer, input_tensor.to(device), max_new_tokens, use_cuda_graph, mode = mode, force_think = force_think,
)
diff --git a/ktransformers/operators/attention.py b/ktransformers/operators/attention.py
index b4c54026..79618dc2 100644
--- a/ktransformers/operators/attention.py
+++ b/ktransformers/operators/attention.py
@@ -19,7 +19,8 @@
import logging
from transformers.configuration_utils import PretrainedConfig
from transformers.cache_utils import Cache
-from flash_attn import flash_attn_func
+if not torch.xpu.is_available():
+ from flash_attn import flash_attn_func
from ktransformers.operators.triton_attention import decode_attention_fwd_grouped
import os
from ktransformers.operators.flashinfer_wrapper import flashinfer_enabled
@@ -494,7 +495,7 @@ def forward_linux_flashinfer(
attn_output = self.o_proj(attn_output)
return attn_output, None, past_key_value
- def forward_windows(
+ def forward_native(
self,
hidden_states: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
@@ -571,8 +572,8 @@ def forward(
cache_position: Optional[torch.LongTensor] = None,
**kwargs,
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- if os.name == 'nt':
- return self.forward_windows(
+ if os.name == 'nt' or torch.xpu.is_available():
+ return self.forward_native(
hidden_states,
attention_mask,
position_ids,
diff --git a/ktransformers/operators/linear.py b/ktransformers/operators/linear.py
index 675a1a9e..fe7c2013 100644
--- a/ktransformers/operators/linear.py
+++ b/ktransformers/operators/linear.py
@@ -21,13 +21,14 @@
from ktransformers.util.custom_gguf import GGUFLoader
from ktransformers.util.utils import InferenceState
-from ktransformers.ktransformers_ext.operators.custom_marlin.quantize.utils.marlin_utils import (
- MarlinWorkspace,
- marlin_quantize,
- GPTQ_MARLIN_MIN_THREAD_N,
- GPTQ_MARLIN_MIN_THREAD_K,
- GPTQ_MARLIN_MAX_PARALLEL,
-)
+if not torch.xpu.is_available():
+ from ktransformers.ktransformers_ext.operators.custom_marlin.quantize.utils.marlin_utils import (
+ MarlinWorkspace,
+ marlin_quantize,
+ GPTQ_MARLIN_MIN_THREAD_N,
+ GPTQ_MARLIN_MIN_THREAD_K,
+ GPTQ_MARLIN_MAX_PARALLEL,
+ )
from ktransformers.operators.base_operator import BaseInjectedModule
from transformers.configuration_utils import PretrainedConfig
diff --git a/ktransformers/optimize/optimize.py b/ktransformers/optimize/optimize.py
index 290b57c6..ab1bc089 100644
--- a/ktransformers/optimize/optimize.py
+++ b/ktransformers/optimize/optimize.py
@@ -76,12 +76,10 @@ def gen_optimize_config(module: nn.Module, out_data: Mapping, rule_list: List, p
if "replace" in rule:
replace_meta = rule["replace"]
if module_name not in out_data:
-
out_data[module_name]={"key": translated_name,
"class": replace_meta["class"] if "class" in replace_meta else "default",
# "device": replace_meta["device"] if "device" in replace_meta else default_device,
"kwargs": copy.deepcopy(replace_meta["kwargs"]) if "kwargs" in replace_meta else dict()}
- print("after:", out_data[module_name])
else:
if out_data[module_name]["class"] == "default":
out_data[module_name]["class"] = replace_meta["class"] if "class" in replace_meta else "default"
@@ -98,7 +96,7 @@ def gen_optimize_config(module: nn.Module, out_data: Mapping, rule_list: List, p
"prefill_device": default_device}
}
- print(out_data[module_name])
+ #print(out_data[module_name])
#input()
if recursive:
@@ -125,7 +123,6 @@ def optimize_and_load_gguf(module: nn.Module, rule_file: str, gguf_path: str, mo
rule_list = yaml.load(f.read(), Loader=yaml.FullLoader)
optimize_config = dict()
- print("###########Default device###########", default_device)
gen_optimize_config(module, optimize_config, rule_list, default_device = default_device)
model_config = translate_model_config(model_config)
diff --git a/ktransformers/util/custom_gguf.py b/ktransformers/util/custom_gguf.py
index 1c6b5081..80e6287a 100644
--- a/ktransformers/util/custom_gguf.py
+++ b/ktransformers/util/custom_gguf.py
@@ -24,7 +24,8 @@
import os
from enum import IntEnum
import torch
-import KTransformersOps
+if not torch.xpu.is_available():
+ import KTransformersOps
import ctypes
class GGMLQuantizationType(IntEnum):
diff --git a/setup.py b/setup.py
index 9f77548e..3c6eea44 100644
--- a/setup.py
+++ b/setup.py
@@ -36,6 +36,8 @@
except ImportError:
MUSA_HOME=None
+KTRANSFORMERS_BUILD_XPU = torch.xpu.is_available()
+
class CpuInstructInfo:
CPU_INSTRUCT = os.getenv("CPU_INSTRUCT", "NATIVE")
FANCY = "FANCY"
@@ -151,8 +153,8 @@ def get_package_version(self, full_version=False):
backend_version = f"cu{self.get_cuda_bare_metal_version(CUDA_HOME)}"
elif MUSA_HOME is not None:
backend_version = f"mu{self.get_musa_bare_metal_version(MUSA_HOME)}"
- elif torch.xpu.is_available()
- backend_version = f"mu{xpu+}"
+ elif torch.xpu.is_available():
+ backend_version = f"xpu"
else:
raise ValueError("Unsupported backend: CUDA_HOME and MUSA_HOME are not set.")
package_version = f"{flash_version}+{backend_version}torch{torch_version}{cpu_instruct}"
@@ -250,8 +252,10 @@ def build_extension(self, ext) -> None:
cmake_args += ["-DKTRANSFORMERS_USE_CUDA=ON"]
elif MUSA_HOME is not None:
cmake_args += ["-DKTRANSFORMERS_USE_MUSA=ON"]
+ elif KTRANSFORMERS_BUILD_XPU:
+ cmake_args += ["-DKTRANSFORMERS_USE_XPU=ON"]
else:
- raise ValueError("Unsupported backend: CUDA_HOME and MUSA_HOME are not set.")
+ raise ValueError("Unsupported backend: CUDA_HOME or MUSA_HOME are not set.")
build_args = []
if "CMAKE_ARGS" in os.environ:
@@ -370,16 +374,26 @@ def build_extension(self, ext) -> None:
]
}
)
-elif torch.xpu.is_available()
- ops_module = None #XPUExtension is not available now.
+elif torch.xpu.is_available():#XPUExtension is not available now.
+ pass
else:
raise ValueError("Unsupported backend: CUDA_HOME and MUSA_HOME are not set.")
-setup(
- version=VersionInfo().get_package_version(),
- cmdclass={"bdist_wheel":BuildWheelsCommand ,"build_ext": CMakeBuild},
- ext_modules=[
- CMakeExtension("cpuinfer_ext"),
- ops_module,
- ]
-)
+if torch.xpu.is_available():
+ setup(
+ version=VersionInfo().get_package_version(),
+ cmdclass={"bdist_wheel":BuildWheelsCommand ,"build_ext": CMakeBuild},
+ ext_modules=[
+ CMakeExtension("cpuinfer_ext"),
+ ]
+ )
+else:
+ setup(
+ version=VersionInfo().get_package_version(),
+ cmdclass={"bdist_wheel":BuildWheelsCommand ,"build_ext": CMakeBuild},
+ ext_modules=[
+ CMakeExtension("cpuinfer_ext"),
+ ops_module,
+ ]
+ )
+
From 58f9d26b26770ad11e1673d39baa597ddfce9d55 Mon Sep 17 00:00:00 2001
From: "Zhang, Liangang"
Date: Tue, 25 Feb 2025 16:34:14 +0800
Subject: [PATCH 04/14] Update CMakeLists.txt
---
ktransformers/ktransformers_ext/CMakeLists.txt | 2 --
1 file changed, 2 deletions(-)
diff --git a/ktransformers/ktransformers_ext/CMakeLists.txt b/ktransformers/ktransformers_ext/CMakeLists.txt
index a207c9ab..ecce9b70 100644
--- a/ktransformers/ktransformers_ext/CMakeLists.txt
+++ b/ktransformers/ktransformers_ext/CMakeLists.txt
@@ -207,7 +207,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/pybind11 ${CMAKE_
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/llama.cpp ${CMAKE_CURRENT_BINARY_DIR}/third_party/llama.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party)
-
if (WIN32)
include_directories("$ENV{CUDA_PATH}/include")
elseif (UNIX)
@@ -248,7 +247,6 @@ message(STATUS "ALL_SOURCES: ${ALL_SOURCES}")
pybind11_add_module(${PROJECT_NAME} MODULE ${ALL_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE llama)
-
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE "$ENV{CUDA_PATH}/lib/x64/cudart.lib")#CUDA::cudart
elseif(UNIX)
From 557d8d1beb12dc838e6dde630c5834b8daee262b Mon Sep 17 00:00:00 2001
From: "Zhang, Liangang"
Date: Tue, 25 Feb 2025 16:34:54 +0800
Subject: [PATCH 05/14] Update local_chat.py
---
ktransformers/local_chat.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ktransformers/local_chat.py b/ktransformers/local_chat.py
index 44a01c40..de86c3dc 100644
--- a/ktransformers/local_chat.py
+++ b/ktransformers/local_chat.py
@@ -180,7 +180,7 @@ def local_chat(
)
else:
generated = prefill_and_generate(
- model, tokenizer, input_tensor.to(device), max_new_tokens, use_cuda_graph, mode = mode, force_think = force_think,
+ model, tokenizer, input_tensor.to(device), max_new_tokens, use_cuda_graph, mode = mode, force_think = force_think,
)
From c89f745cf7a7273934e1cfb2fbd0fe00759ed7cb Mon Sep 17 00:00:00 2001
From: root
Date: Wed, 26 Feb 2025 02:11:43 +0000
Subject: [PATCH 06/14] Fix rebase issue
---
ktransformers/local_chat.py | 2 +-
ktransformers/operators/attention.py | 2 +-
ktransformers/operators/models.py | 3 +--
ktransformers/optimize/optimize.py | 2 ++
ktransformers/util/custom_loader.py | 5 +++--
ktransformers/util/utils.py | 5 ++++-
6 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/ktransformers/local_chat.py b/ktransformers/local_chat.py
index 1f88dc38..ae0650f3 100644
--- a/ktransformers/local_chat.py
+++ b/ktransformers/local_chat.py
@@ -112,7 +112,7 @@ def local_chat(
gguf_path = input(
"please input the path of your gguf file(gguf file in the dir containing input gguf file must all belong to current model):"
)
- optimize_and_load_gguf(model, optimize_config_path, gguf_path, config)
+ optimize_and_load_gguf(model, optimize_config_path, gguf_path, config, default_device=device)
try:
model.generation_config = GenerationConfig.from_pretrained(model_path)
diff --git a/ktransformers/operators/attention.py b/ktransformers/operators/attention.py
index d0d075c4..76344dd6 100644
--- a/ktransformers/operators/attention.py
+++ b/ktransformers/operators/attention.py
@@ -590,7 +590,7 @@ def forward(
cache_position: Optional[torch.LongTensor] = None,
**kwargs,
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- if os.name == 'nt' or get_compute_capability()<8 or torch.xpu.is_available():
+ if os.name == 'nt' or torch.xpu.is_available or get_compute_capability()<8:
return self.forward_native(
hidden_states,
attention_mask,
diff --git a/ktransformers/operators/models.py b/ktransformers/operators/models.py
index 57d4bea0..c05b7c9f 100644
--- a/ktransformers/operators/models.py
+++ b/ktransformers/operators/models.py
@@ -649,8 +649,7 @@ def forward(
if per_layer_prefill_flag:
causal_mask = None
else:
- if os.name == 'nt' or get_compute_capability()<8:
- print("for Windows or GPU before ampere, use forward_windows")
+ if os.name == 'nt' or torch.xpu.is_available() or get_compute_capability()<8:
# only use mask in forward windows or can't flash attn
causal_mask = self._update_causal_mask(
attention_mask, inputs_embeds, cache_position, past_key_values, output_attentions
diff --git a/ktransformers/optimize/optimize.py b/ktransformers/optimize/optimize.py
index ab1bc089..85f865a9 100644
--- a/ktransformers/optimize/optimize.py
+++ b/ktransformers/optimize/optimize.py
@@ -122,6 +122,8 @@ def optimize_and_load_gguf(module: nn.Module, rule_file: str, gguf_path: str, mo
with open(rule_file, 'r', encoding='utf-8') as f:
rule_list = yaml.load(f.read(), Loader=yaml.FullLoader)
+ import pdb
+ pdb.set_trace()
optimize_config = dict()
gen_optimize_config(module, optimize_config, rule_list, default_device = default_device)
diff --git a/ktransformers/util/custom_loader.py b/ktransformers/util/custom_loader.py
index ecc09a0a..e20b79fd 100644
--- a/ktransformers/util/custom_loader.py
+++ b/ktransformers/util/custom_loader.py
@@ -7,7 +7,8 @@
import os
from enum import IntEnum
import torch
-import KTransformersOps
+if not torch.xpu.is_available():
+ import KTransformersOps
from safetensors import safe_open
from ktransformers.ktransformers_ext.triton.fp8gemm import fp8_gemm, act_quant, weight_dequant
from safetensors.torch import save_file
@@ -83,4 +84,4 @@ def load_dequantized_tensor(self, key:str, device: str="cpu"):
if key[:-7] + ".weight_scale_inv" in self.tensor_file_map:
weight_scale_inv = f.get_tensor(key[:-7] + ".weight_scale_inv").to(device)
tensor = weight_dequant(tensor, weight_scale_inv)
- return tensor.to(device)
\ No newline at end of file
+ return tensor.to(device)
diff --git a/ktransformers/util/utils.py b/ktransformers/util/utils.py
index 313950c0..8d9e4c49 100644
--- a/ktransformers/util/utils.py
+++ b/ktransformers/util/utils.py
@@ -92,7 +92,10 @@ def load_cur_state_dict(module: nn.Module, gguf_loader: GGUFLoader, prefix: str
target_dtype = torch.get_default_dtype()
device = get_device(translated_key[:translated_key.rfind(".")], gguf_loader.tensor_device_map)
print(f"loading {translated_key} to {device}")
- torch.cuda.empty_cache() # To fit in 16G VRAM. By "wkGCaSS - 知乎 https://zhuanlan.zhihu.com/p/25491611225"
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache() # To fit in 16G VRAM. By "wkGCaSS - 知乎 https://zhuanlan.zhihu.com/p/25491611225"
+ elif torch.xpu.is_available():
+ torch.cuda.empty_cache()
# weights = gguf_loader.load_gguf_tensor(translated_key, device = device).to(dtype = target_dtype)
weights = load_dequantized_tensor(translated_key, device=device).to(dtype=target_dtype)
set_param(module, name, weights)
From cd4500af4f42d6d34ffd2acd9f033412557bd351 Mon Sep 17 00:00:00 2001
From: "Zhang, Liangang"
Date: Wed, 26 Feb 2025 10:43:31 +0800
Subject: [PATCH 07/14] Update optimize.py
---
ktransformers/optimize/optimize.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/ktransformers/optimize/optimize.py b/ktransformers/optimize/optimize.py
index 85f865a9..ab1bc089 100644
--- a/ktransformers/optimize/optimize.py
+++ b/ktransformers/optimize/optimize.py
@@ -122,8 +122,6 @@ def optimize_and_load_gguf(module: nn.Module, rule_file: str, gguf_path: str, mo
with open(rule_file, 'r', encoding='utf-8') as f:
rule_list = yaml.load(f.read(), Loader=yaml.FullLoader)
- import pdb
- pdb.set_trace()
optimize_config = dict()
gen_optimize_config(module, optimize_config, rule_list, default_device = default_device)
From 247711c532bdaba90639af07a6f9b28a850f537e Mon Sep 17 00:00:00 2001
From: "Zhang, Liangang"
Date: Wed, 26 Feb 2025 10:58:01 +0800
Subject: [PATCH 08/14] Update attention.py
---
ktransformers/operators/attention.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ktransformers/operators/attention.py b/ktransformers/operators/attention.py
index 76344dd6..aea5a31f 100644
--- a/ktransformers/operators/attention.py
+++ b/ktransformers/operators/attention.py
@@ -590,7 +590,7 @@ def forward(
cache_position: Optional[torch.LongTensor] = None,
**kwargs,
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- if os.name == 'nt' or torch.xpu.is_available or get_compute_capability()<8:
+ if os.name == 'nt' or torch.xpu.is_available() or get_compute_capability()<8:
return self.forward_native(
hidden_states,
attention_mask,
From 0b6fecfc6c330fab30faea5b0708c75bb634b3f7 Mon Sep 17 00:00:00 2001
From: "Zhang,Liangang"
Date: Wed, 26 Feb 2025 15:48:56 -0800
Subject: [PATCH 09/14] Fix cuda graph issue
---
ktransformers/.flake8 | 4 +
.../workflows/package_wheel_release.yml | 229 +
.../.github/workflows/package_wheel_test.yml | 141 +
ktransformers/.gitignore | 24 +
ktransformers/.gitmodules | 6 +
ktransformers/.pylintrc | 6 +
ktransformers/Dockerfile | 35 +
ktransformers/LICENSE | 201 +
ktransformers/MANIFEST.in | 11 +
ktransformers/Makefile | 21 +
ktransformers/README.md | 382 +
ktransformers/WeChatGroup.png | Bin 0 -> 849325 bytes
ktransformers/doc/assets/BigCodeBench.png | Bin 0 -> 990743 bytes
.../doc/assets/DeepSeek-on-KTransformers.PNG | Bin 0 -> 1290382 bytes
ktransformers/doc/assets/Framework_effect.png | Bin 0 -> 111136 bytes
ktransformers/doc/assets/InfLLM_equation.jpg | Bin 0 -> 17725 bytes
ktransformers/doc/assets/InfLLM_framework.png | Bin 0 -> 120894 bytes
ktransformers/doc/assets/InjectStruction.png | Bin 0 -> 318951 bytes
ktransformers/doc/assets/KTransformers.png | Bin 0 -> 31761 bytes
.../assets/KTransformers_long_context_v1.png | Bin 0 -> 134536 bytes
.../assets/KTransformers_long_context_v2.png | Bin 0 -> 161040 bytes
ktransformers/doc/assets/Quest_framework.png | Bin 0 -> 290850 bytes
ktransformers/doc/assets/SnapKV_framework.png | Bin 0 -> 214311 bytes
ktransformers/doc/assets/SparQ_attention.png | Bin 0 -> 140228 bytes
ktransformers/doc/assets/cpuinfer.png | Bin 0 -> 148670 bytes
.../doc/assets/deepseekv2_structure.png | Bin 0 -> 106714 bytes
ktransformers/doc/assets/internlm_memory.png | Bin 0 -> 127690 bytes
.../doc/assets/long_context_generate.png | Bin 0 -> 185748 bytes
.../doc/assets/long_context_prefill.png | Bin 0 -> 150325 bytes
.../doc/assets/model_structure_guild.png | Bin 0 -> 281048 bytes
ktransformers/doc/assets/multi_gpu.png | Bin 0 -> 104155 bytes
ktransformers/doc/assets/needle_128K.png | Bin 0 -> 138555 bytes
ktransformers/doc/assets/needle_1M.png | Bin 0 -> 103188 bytes
ktransformers/doc/assets/website.png | Bin 0 -> 514143 bytes
.../doc/en/DeepseekR1_V3_tutorial.md | 178 +
ktransformers/doc/en/Docker.md | 29 +
ktransformers/doc/en/FAQ.md | 69 +
ktransformers/doc/en/api/server/api.md | 108 +
ktransformers/doc/en/api/server/run-tabby.png | Bin 0 -> 25957 bytes
.../doc/en/api/server/server-arch.png | Bin 0 -> 185173 bytes
ktransformers/doc/en/api/server/server.md | 37 +
ktransformers/doc/en/api/server/tabby.md | 33 +
.../doc/en/api/server/visit-api-tags.png | Bin 0 -> 57947 bytes
ktransformers/doc/en/api/server/website.md | 32 +
ktransformers/doc/en/deepseek-v2-injection.md | 170 +
ktransformers/doc/en/injection_tutorial.md | 328 +
.../doc/en/long_context_introduction.md | 316 +
ktransformers/doc/en/long_context_tutorial.md | 41 +
ktransformers/doc/en/makefile_usage.md | 26 +
.../operators/Combined_MoE_time_per_layer.png | Bin 0 -> 377855 bytes
.../en/operators/Linear_projection_time.png | Bin 0 -> 209342 bytes
ktransformers/doc/en/operators/llamafile.md | 35 +
ktransformers/doc/zh/api/server/api.md | 115 +
ktransformers/doc/zh/api/server/run-tabby.png | Bin 0 -> 25957 bytes
.../doc/zh/api/server/server-arch.png | Bin 0 -> 185173 bytes
ktransformers/doc/zh/api/server/server.md | 41 +
ktransformers/doc/zh/api/server/tabby.md | 34 +
.../doc/zh/api/server/visit-api-tags.png | Bin 0 -> 57947 bytes
ktransformers/doc/zh/api/server/website.md | 32 +
ktransformers/install.bat | 16 +
ktransformers/install.sh | 15 +
ktransformers/ktransformers/__init__.py | 11 +
.../ktransformers/configs/config.yaml | 57 +
.../ktransformers/configs/log_config.ini | 46 +
.../ktransformers_ext/CMakeLists.txt | 254 +
.../bench/bench_attention.py | 178 +
.../bench/bench_attention_torch.py | 94 +
.../ktransformers_ext/bench/bench_linear.py | 121 +
.../bench/bench_linear_torch.py | 83 +
.../ktransformers_ext/bench/bench_mlp.py | 150 +
.../bench/bench_mlp_torch.py | 110 +
.../ktransformers_ext/bench/bench_moe.py | 160 +
.../bench/bench_moe_torch.py | 152 +
.../ktransformers_ext/cmake/FindSIMD.cmake | 100 +
.../ktransformers_ext/cpu_backend/backend.cpp | 149 +
.../ktransformers_ext/cpu_backend/backend.h | 58 +
.../ktransformers_ext/cpu_backend/cpuinfer.h | 92 +
.../cpu_backend/task_queue.cpp | 67 +
.../cpu_backend/task_queue.h | 138 +
.../ktransformers_ext/cuda/binding.cpp | 40 +
.../cuda/custom_gguf/binding.cpp | 35 +
.../cuda/custom_gguf/dequant.cu | 387 +
.../ktransformers_ext/cuda/custom_gguf/ops.h | 22 +
.../cuda/gptq_marlin/gptq_marlin.cu | 1907 ++
.../cuda/gptq_marlin/gptq_marlin.cuh | 80 +
.../cuda/gptq_marlin/gptq_marlin_dtypes.cuh | 80 +
.../ktransformers_ext/cuda/gptq_marlin/ops.h | 24 +
.../ktransformers_ext/cuda/setup.py | 26 +
.../examples/test_attention.py | 142 +
.../ktransformers_ext/examples/test_linear.py | 62 +
.../ktransformers_ext/examples/test_mlp.py | 82 +
.../ktransformers_ext/examples/test_moe.py | 121 +
.../ktransformers_ext/ext_bindings.cpp | 685 +
.../custom_marlin/quantize/utils/__init__.py | 0
.../custom_marlin/quantize/utils/format_24.py | 308 +
.../quantize/utils/marlin_24_perms.py | 60 +
.../quantize/utils/marlin_perms.py | 60 +
.../quantize/utils/marlin_utils.py | 232 +
.../quantize/utils/quant_utils.py | 146 +
.../operators/kvcache/kvcache.h | 727 +
.../operators/kvcache/kvcache_attn.cpp | 2533 ++
.../operators/kvcache/kvcache_load_dump.cpp | 123 +
.../operators/kvcache/kvcache_read_write.cpp | 1019 +
.../operators/kvcache/kvcache_utils.cpp | 1157 +
.../operators/llamafile/conversion.h | 32 +
.../operators/llamafile/linear.cpp | 77 +
.../operators/llamafile/linear.h | 59 +
.../operators/llamafile/mlp.cpp | 125 +
.../operators/llamafile/mlp.h | 70 +
.../operators/llamafile/moe.cpp | 354 +
.../operators/llamafile/moe.h | 103 +
.../operators/llamafile/shared_mem_buffer.cpp | 55 +
.../operators/llamafile/shared_mem_buffer.h | 37 +
ktransformers/ktransformers/local_chat.py | 180 +
.../ktransformers/models/__init__.py | 0
.../models/configuration_deepseek.py | 207 +
.../models/configuration_deepseek_v3.py | 235 +
.../models/configuration_llama.py | 203 +
.../ktransformers/models/custom_cache.py | 141 +
.../ktransformers/models/modeling_deepseek.py | 1992 ++
.../models/modeling_deepseek_v3.py | 1936 ++
.../ktransformers/models/modeling_llama.py | 1744 ++
.../ktransformers/models/modeling_mixtral.py | 1735 ++
.../models/modeling_qwen2_moe.py | 1765 ++
ktransformers/ktransformers/operators/RoPE.py | 360 +
.../ktransformers/operators/__init__.py | 1 +
.../ktransformers/operators/attention.py | 554 +
.../ktransformers/operators/base_operator.py | 60 +
.../ktransformers/operators/cpuinfer.py | 746 +
.../operators/dynamic_attention.py | 775 +
.../ktransformers/operators/experts.py | 951 +
ktransformers/ktransformers/operators/gate.py | 126 +
.../ktransformers/operators/linear.py | 449 +
.../ktransformers/operators/models.py | 1345 +
.../ktransformers/optimize/optimize.py | 135 +
.../optimize_rules/DeepSeek-V2-Chat-XPU.yaml | 56 +
.../DeepSeek-V2-Chat-multi-gpu-4.yaml | 228 +
.../DeepSeek-V2-Chat-multi-gpu.yaml | 126 +
.../optimize_rules/DeepSeek-V2-Chat.yaml | 56 +
.../DeepSeek-V2-Lite-Chat-multi-gpu.yaml | 126 +
.../optimize_rules/DeepSeek-V2-Lite-Chat.yaml | 56 +
.../DeepSeek-V3-Chat-multi-gpu-marlin.yaml | 161 +
.../DeepSeek-V3-Chat-multi-gpu.yaml | 143 +
.../optimize_rules/DeepSeek-V3-Chat.yaml | 63 +
.../Internlm2_5-7b-Chat-1m.yaml | 28 +
.../optimize/optimize_rules/Mixtral.yaml | 49 +
.../Qwen2-57B-A14B-Instruct-multi-gpu.yaml | 112 +
.../Qwen2-57B-A14B-Instruct.yaml | 57 +
.../ktransformers/server/__init__.py | 0
.../ktransformers/server/api/__init__.py | 10 +
.../server/api/ollama/__init__.py | 6 +
.../server/api/ollama/completions.py | 162 +
.../server/api/openai/__init__.py | 15 +
.../server/api/openai/assistants/__init__.py | 14 +
.../api/openai/assistants/assistants.py | 103 +
.../server/api/openai/assistants/messages.py | 54 +
.../server/api/openai/assistants/runs.py | 99 +
.../server/api/openai/assistants/threads.py | 36 +
.../server/api/openai/endpoints/__init__.py | 0
.../server/api/openai/endpoints/chat.py | 42 +
.../server/api/openai/legacy/__init__.py | 6 +
.../server/api/openai/legacy/completions.py | 33 +
.../ktransformers/server/api/web/__init__.py | 6 +
.../ktransformers/server/api/web/system.py | 9 +
ktransformers/ktransformers/server/args.py | 126 +
.../ktransformers/server/backend/__init__.py | 0
.../ktransformers/server/backend/args.py | 89 +
.../ktransformers/server/backend/base.py | 162 +
.../server/backend/context_manager.py | 54 +
.../server/backend/interfaces/__init__.py | 0
.../server/backend/interfaces/exllamav2.py | 40 +
.../backend/interfaces/ktransformers.py | 174 +
.../server/backend/interfaces/transformers.py | 353 +
.../ktransformers/server/config/config.py | 187 +
.../ktransformers/server/config/log.py | 176 +
.../ktransformers/server/config/singleton.py | 35 +
.../ktransformers/server/crud/__init__.py | 0
.../server/crud/assistants/__init__.py | 0
.../server/crud/assistants/assistants.py | 66 +
.../server/crud/assistants/messages.py | 86 +
.../server/crud/assistants/runs.py | 50 +
.../server/crud/assistants/threads.py | 93 +
.../ktransformers/server/exceptions.py | 23 +
ktransformers/ktransformers/server/main.py | 125 +
.../ktransformers/server/models/__init__.py | 0
.../server/models/assistants/__init__.py | 0
.../server/models/assistants/assistants.py | 29 +
.../server/models/assistants/messages.py | 28 +
.../server/models/assistants/run_steps.py | 31 +
.../server/models/assistants/runs.py | 39 +
.../server/models/assistants/threads.py | 18 +
.../ktransformers/server/requirements.txt | 13 +
.../ktransformers/server/schemas/__init__.py | 0
.../server/schemas/assistants/__init__.py | 0
.../server/schemas/assistants/assistants.py | 202 +
.../server/schemas/assistants/messages.py | 213 +
.../server/schemas/assistants/runs.py | 201 +
.../server/schemas/assistants/streaming.py | 169 +
.../server/schemas/assistants/threads.py | 49 +
.../server/schemas/assistants/tool.py | 54 +
.../ktransformers/server/schemas/base.py | 46 +
.../server/schemas/conversation.py | 12 +
.../server/schemas/endpoints/chat.py | 78 +
.../server/schemas/legacy/__init__.py | 0
.../server/schemas/legacy/completions.py | 48 +
.../ktransformers/server/utils/__init__.py | 0
.../server/utils/create_interface.py | 38 +
.../ktransformers/server/utils/multi_timer.py | 79 +
.../ktransformers/server/utils/sql_utils.py | 128 +
.../ktransformers/tests/dequant_gpu.py | 58 +
.../ktransformers/tests/dequant_gpu_t.py | 40 +
.../ktransformers/util/cuda_graph_runner.py | 84 +
.../ktransformers/util/custom_gguf.py | 830 +
.../ktransformers/util/modeling_rope_utils.py | 592 +
.../ktransformers/util/textstream.py | 84 +
ktransformers/ktransformers/util/utils.py | 227 +
.../ktransformers/website/.browserslistrc | 4 +
.../ktransformers/website/.eslintrc.js | 29 +
.../ktransformers/website/.gitignore | 23 +
ktransformers/ktransformers/website/README.md | 29 +
.../ktransformers/website/config.d.ts | 7 +
.../ktransformers/website/jest.config.js | 3 +
.../ktransformers/website/package-lock.json | 24731 ++++++++++++++++
.../ktransformers/website/package.json | 61 +
.../ktransformers/website/public/balck.ico | Bin 0 -> 4286 bytes
.../ktransformers/website/public/config.js | 4 +
.../website/public/css/reset.css | 67 +
.../public/images/assistant-avatar.png | Bin 0 -> 1258 bytes
.../website/public/images/avatar.png | Bin 0 -> 996 bytes
.../website/public/images/bgbg.png | Bin 0 -> 6290 bytes
.../website/public/images/logo.ico | Bin 0 -> 4286 bytes
.../website/public/images/logo.png | Bin 0 -> 14178 bytes
.../website/public/images/three.png | Bin 0 -> 2189 bytes
.../website/public/images/user-filling.png | Bin 0 -> 1681 bytes
.../ktransformers/website/public/index.html | 19 +
.../ktransformers/website/src/App.vue | 19 +
.../website/src/api/api-client.ts | 11 +
.../website/src/api/assistant.ts | 184 +
.../ktransformers/website/src/api/message.ts | 75 +
.../ktransformers/website/src/api/run.ts | 106 +
.../ktransformers/website/src/api/thread.ts | 48 +
.../website/src/assets/css/mixins.styl | 161 +
.../website/src/assets/iconfont/demo.css | 539 +
.../src/assets/iconfont/demo_index.html | 557 +
.../website/src/assets/iconfont/iconfont.css | 80 +
.../website/src/assets/iconfont/iconfont.js | 1 +
.../website/src/assets/iconfont/iconfont.json | 121 +
.../website/src/assets/iconfont/iconfont.svg | 51 +
.../website/src/assets/iconfont/iconfont.ttf | Bin 0 -> 4608 bytes
.../website/src/assets/iconfont/iconfont.woff | Bin 0 -> 2944 bytes
.../src/assets/iconfont/iconfont.woff2 | Bin 0 -> 2360 bytes
.../website/src/components/chat/index.vue | 800 +
.../ktransformers/website/src/conf/config.ts | 11 +
.../ktransformers/website/src/locals/en.js | 58 +
.../ktransformers/website/src/locals/index.js | 18 +
.../ktransformers/website/src/locals/zh.js | 58 +
.../ktransformers/website/src/main.ts | 18 +
.../ktransformers/website/src/router/index.ts | 24 +
.../ktransformers/website/src/shims-vue.d.ts | 10 +
.../ktransformers/website/src/store/index.ts | 14 +
.../ktransformers/website/src/utils/copy.ts | 111 +
.../ktransformers/website/src/utils/types.ts | 125 +
.../ktransformers/website/src/views/home.vue | 718 +
.../website/tests/unit/example.spec.ts | 12 +
.../ktransformers/website/tsconfig.json | 45 +
.../ktransformers/website/vue.config.js | 40 +
.../cpu_backend/.cpuinfer.h.swp | Bin 0 -> 12288 bytes
.../ktransformers_ext/cpu_backend/cpuinfer.h | 10 +-
.../ktransformers_ext/ext_bindings.cpp | 2 +-
ktransformers/optimize/.optimize.py.swp | Bin 0 -> 16384 bytes
ktransformers/pyproject.toml | 76 +
ktransformers/requirements-local_chat.txt | 7 +
ktransformers/setup.py | 324 +
273 files changed, 67835 insertions(+), 6 deletions(-)
create mode 100644 ktransformers/.flake8
create mode 100644 ktransformers/.github/workflows/package_wheel_release.yml
create mode 100644 ktransformers/.github/workflows/package_wheel_test.yml
create mode 100644 ktransformers/.gitignore
create mode 100644 ktransformers/.gitmodules
create mode 100644 ktransformers/.pylintrc
create mode 100644 ktransformers/Dockerfile
create mode 100644 ktransformers/LICENSE
create mode 100644 ktransformers/MANIFEST.in
create mode 100644 ktransformers/Makefile
create mode 100644 ktransformers/README.md
create mode 100644 ktransformers/WeChatGroup.png
create mode 100644 ktransformers/doc/assets/BigCodeBench.png
create mode 100644 ktransformers/doc/assets/DeepSeek-on-KTransformers.PNG
create mode 100644 ktransformers/doc/assets/Framework_effect.png
create mode 100644 ktransformers/doc/assets/InfLLM_equation.jpg
create mode 100644 ktransformers/doc/assets/InfLLM_framework.png
create mode 100644 ktransformers/doc/assets/InjectStruction.png
create mode 100644 ktransformers/doc/assets/KTransformers.png
create mode 100644 ktransformers/doc/assets/KTransformers_long_context_v1.png
create mode 100644 ktransformers/doc/assets/KTransformers_long_context_v2.png
create mode 100644 ktransformers/doc/assets/Quest_framework.png
create mode 100644 ktransformers/doc/assets/SnapKV_framework.png
create mode 100644 ktransformers/doc/assets/SparQ_attention.png
create mode 100644 ktransformers/doc/assets/cpuinfer.png
create mode 100644 ktransformers/doc/assets/deepseekv2_structure.png
create mode 100644 ktransformers/doc/assets/internlm_memory.png
create mode 100644 ktransformers/doc/assets/long_context_generate.png
create mode 100644 ktransformers/doc/assets/long_context_prefill.png
create mode 100644 ktransformers/doc/assets/model_structure_guild.png
create mode 100644 ktransformers/doc/assets/multi_gpu.png
create mode 100644 ktransformers/doc/assets/needle_128K.png
create mode 100644 ktransformers/doc/assets/needle_1M.png
create mode 100644 ktransformers/doc/assets/website.png
create mode 100644 ktransformers/doc/en/DeepseekR1_V3_tutorial.md
create mode 100644 ktransformers/doc/en/Docker.md
create mode 100644 ktransformers/doc/en/FAQ.md
create mode 100644 ktransformers/doc/en/api/server/api.md
create mode 100644 ktransformers/doc/en/api/server/run-tabby.png
create mode 100644 ktransformers/doc/en/api/server/server-arch.png
create mode 100644 ktransformers/doc/en/api/server/server.md
create mode 100644 ktransformers/doc/en/api/server/tabby.md
create mode 100644 ktransformers/doc/en/api/server/visit-api-tags.png
create mode 100644 ktransformers/doc/en/api/server/website.md
create mode 100644 ktransformers/doc/en/deepseek-v2-injection.md
create mode 100644 ktransformers/doc/en/injection_tutorial.md
create mode 100644 ktransformers/doc/en/long_context_introduction.md
create mode 100644 ktransformers/doc/en/long_context_tutorial.md
create mode 100644 ktransformers/doc/en/makefile_usage.md
create mode 100644 ktransformers/doc/en/operators/Combined_MoE_time_per_layer.png
create mode 100644 ktransformers/doc/en/operators/Linear_projection_time.png
create mode 100644 ktransformers/doc/en/operators/llamafile.md
create mode 100644 ktransformers/doc/zh/api/server/api.md
create mode 100644 ktransformers/doc/zh/api/server/run-tabby.png
create mode 100644 ktransformers/doc/zh/api/server/server-arch.png
create mode 100644 ktransformers/doc/zh/api/server/server.md
create mode 100644 ktransformers/doc/zh/api/server/tabby.md
create mode 100644 ktransformers/doc/zh/api/server/visit-api-tags.png
create mode 100644 ktransformers/doc/zh/api/server/website.md
create mode 100644 ktransformers/install.bat
create mode 100644 ktransformers/install.sh
create mode 100644 ktransformers/ktransformers/__init__.py
create mode 100644 ktransformers/ktransformers/configs/config.yaml
create mode 100644 ktransformers/ktransformers/configs/log_config.ini
create mode 100644 ktransformers/ktransformers/ktransformers_ext/CMakeLists.txt
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_attention.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_attention_torch.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_linear.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_linear_torch.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_mlp.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_mlp_torch.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_moe.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/bench/bench_moe_torch.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cmake/FindSIMD.cmake
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cpu_backend/backend.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cpu_backend/backend.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cpu_backend/cpuinfer.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cpu_backend/task_queue.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cpu_backend/task_queue.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/binding.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/custom_gguf/binding.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/custom_gguf/dequant.cu
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/custom_gguf/ops.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/gptq_marlin/gptq_marlin.cu
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/gptq_marlin/gptq_marlin.cuh
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/gptq_marlin/gptq_marlin_dtypes.cuh
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/gptq_marlin/ops.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/cuda/setup.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/examples/test_attention.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/examples/test_linear.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/examples/test_mlp.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/examples/test_moe.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/ext_bindings.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/__init__.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/format_24.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/marlin_24_perms.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/marlin_perms.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/marlin_utils.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/custom_marlin/quantize/utils/quant_utils.py
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/kvcache/kvcache.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/kvcache/kvcache_attn.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/kvcache/kvcache_load_dump.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/kvcache/kvcache_read_write.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/kvcache/kvcache_utils.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/conversion.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/linear.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/linear.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/mlp.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/mlp.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/moe.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/moe.h
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/shared_mem_buffer.cpp
create mode 100644 ktransformers/ktransformers/ktransformers_ext/operators/llamafile/shared_mem_buffer.h
create mode 100644 ktransformers/ktransformers/local_chat.py
create mode 100644 ktransformers/ktransformers/models/__init__.py
create mode 100644 ktransformers/ktransformers/models/configuration_deepseek.py
create mode 100644 ktransformers/ktransformers/models/configuration_deepseek_v3.py
create mode 100644 ktransformers/ktransformers/models/configuration_llama.py
create mode 100644 ktransformers/ktransformers/models/custom_cache.py
create mode 100644 ktransformers/ktransformers/models/modeling_deepseek.py
create mode 100644 ktransformers/ktransformers/models/modeling_deepseek_v3.py
create mode 100644 ktransformers/ktransformers/models/modeling_llama.py
create mode 100644 ktransformers/ktransformers/models/modeling_mixtral.py
create mode 100644 ktransformers/ktransformers/models/modeling_qwen2_moe.py
create mode 100644 ktransformers/ktransformers/operators/RoPE.py
create mode 100644 ktransformers/ktransformers/operators/__init__.py
create mode 100644 ktransformers/ktransformers/operators/attention.py
create mode 100644 ktransformers/ktransformers/operators/base_operator.py
create mode 100644 ktransformers/ktransformers/operators/cpuinfer.py
create mode 100644 ktransformers/ktransformers/operators/dynamic_attention.py
create mode 100644 ktransformers/ktransformers/operators/experts.py
create mode 100644 ktransformers/ktransformers/operators/gate.py
create mode 100644 ktransformers/ktransformers/operators/linear.py
create mode 100644 ktransformers/ktransformers/operators/models.py
create mode 100644 ktransformers/ktransformers/optimize/optimize.py
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-XPU.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-multi-gpu-4.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat-multi-gpu.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Chat.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Lite-Chat-multi-gpu.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V2-Lite-Chat.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V3-Chat-multi-gpu-marlin.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V3-Chat-multi-gpu.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/DeepSeek-V3-Chat.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/Internlm2_5-7b-Chat-1m.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/Mixtral.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/Qwen2-57B-A14B-Instruct-multi-gpu.yaml
create mode 100644 ktransformers/ktransformers/optimize/optimize_rules/Qwen2-57B-A14B-Instruct.yaml
create mode 100644 ktransformers/ktransformers/server/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/ollama/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/ollama/completions.py
create mode 100644 ktransformers/ktransformers/server/api/openai/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/openai/assistants/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/openai/assistants/assistants.py
create mode 100644 ktransformers/ktransformers/server/api/openai/assistants/messages.py
create mode 100644 ktransformers/ktransformers/server/api/openai/assistants/runs.py
create mode 100644 ktransformers/ktransformers/server/api/openai/assistants/threads.py
create mode 100644 ktransformers/ktransformers/server/api/openai/endpoints/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/openai/endpoints/chat.py
create mode 100644 ktransformers/ktransformers/server/api/openai/legacy/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/openai/legacy/completions.py
create mode 100644 ktransformers/ktransformers/server/api/web/__init__.py
create mode 100644 ktransformers/ktransformers/server/api/web/system.py
create mode 100644 ktransformers/ktransformers/server/args.py
create mode 100644 ktransformers/ktransformers/server/backend/__init__.py
create mode 100644 ktransformers/ktransformers/server/backend/args.py
create mode 100644 ktransformers/ktransformers/server/backend/base.py
create mode 100644 ktransformers/ktransformers/server/backend/context_manager.py
create mode 100644 ktransformers/ktransformers/server/backend/interfaces/__init__.py
create mode 100644 ktransformers/ktransformers/server/backend/interfaces/exllamav2.py
create mode 100644 ktransformers/ktransformers/server/backend/interfaces/ktransformers.py
create mode 100644 ktransformers/ktransformers/server/backend/interfaces/transformers.py
create mode 100644 ktransformers/ktransformers/server/config/config.py
create mode 100644 ktransformers/ktransformers/server/config/log.py
create mode 100644 ktransformers/ktransformers/server/config/singleton.py
create mode 100644 ktransformers/ktransformers/server/crud/__init__.py
create mode 100644 ktransformers/ktransformers/server/crud/assistants/__init__.py
create mode 100644 ktransformers/ktransformers/server/crud/assistants/assistants.py
create mode 100644 ktransformers/ktransformers/server/crud/assistants/messages.py
create mode 100644 ktransformers/ktransformers/server/crud/assistants/runs.py
create mode 100644 ktransformers/ktransformers/server/crud/assistants/threads.py
create mode 100644 ktransformers/ktransformers/server/exceptions.py
create mode 100644 ktransformers/ktransformers/server/main.py
create mode 100644 ktransformers/ktransformers/server/models/__init__.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/__init__.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/assistants.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/messages.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/run_steps.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/runs.py
create mode 100644 ktransformers/ktransformers/server/models/assistants/threads.py
create mode 100644 ktransformers/ktransformers/server/requirements.txt
create mode 100644 ktransformers/ktransformers/server/schemas/__init__.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/__init__.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/assistants.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/messages.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/runs.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/streaming.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/threads.py
create mode 100644 ktransformers/ktransformers/server/schemas/assistants/tool.py
create mode 100644 ktransformers/ktransformers/server/schemas/base.py
create mode 100644 ktransformers/ktransformers/server/schemas/conversation.py
create mode 100644 ktransformers/ktransformers/server/schemas/endpoints/chat.py
create mode 100644 ktransformers/ktransformers/server/schemas/legacy/__init__.py
create mode 100644 ktransformers/ktransformers/server/schemas/legacy/completions.py
create mode 100644 ktransformers/ktransformers/server/utils/__init__.py
create mode 100644 ktransformers/ktransformers/server/utils/create_interface.py
create mode 100644 ktransformers/ktransformers/server/utils/multi_timer.py
create mode 100644 ktransformers/ktransformers/server/utils/sql_utils.py
create mode 100644 ktransformers/ktransformers/tests/dequant_gpu.py
create mode 100644 ktransformers/ktransformers/tests/dequant_gpu_t.py
create mode 100644 ktransformers/ktransformers/util/cuda_graph_runner.py
create mode 100644 ktransformers/ktransformers/util/custom_gguf.py
create mode 100644 ktransformers/ktransformers/util/modeling_rope_utils.py
create mode 100644 ktransformers/ktransformers/util/textstream.py
create mode 100644 ktransformers/ktransformers/util/utils.py
create mode 100644 ktransformers/ktransformers/website/.browserslistrc
create mode 100644 ktransformers/ktransformers/website/.eslintrc.js
create mode 100644 ktransformers/ktransformers/website/.gitignore
create mode 100644 ktransformers/ktransformers/website/README.md
create mode 100644 ktransformers/ktransformers/website/config.d.ts
create mode 100644 ktransformers/ktransformers/website/jest.config.js
create mode 100644 ktransformers/ktransformers/website/package-lock.json
create mode 100644 ktransformers/ktransformers/website/package.json
create mode 100644 ktransformers/ktransformers/website/public/balck.ico
create mode 100644 ktransformers/ktransformers/website/public/config.js
create mode 100644 ktransformers/ktransformers/website/public/css/reset.css
create mode 100644 ktransformers/ktransformers/website/public/images/assistant-avatar.png
create mode 100644 ktransformers/ktransformers/website/public/images/avatar.png
create mode 100644 ktransformers/ktransformers/website/public/images/bgbg.png
create mode 100644 ktransformers/ktransformers/website/public/images/logo.ico
create mode 100644 ktransformers/ktransformers/website/public/images/logo.png
create mode 100644 ktransformers/ktransformers/website/public/images/three.png
create mode 100644 ktransformers/ktransformers/website/public/images/user-filling.png
create mode 100644 ktransformers/ktransformers/website/public/index.html
create mode 100644 ktransformers/ktransformers/website/src/App.vue
create mode 100644 ktransformers/ktransformers/website/src/api/api-client.ts
create mode 100644 ktransformers/ktransformers/website/src/api/assistant.ts
create mode 100644 ktransformers/ktransformers/website/src/api/message.ts
create mode 100644 ktransformers/ktransformers/website/src/api/run.ts
create mode 100644 ktransformers/ktransformers/website/src/api/thread.ts
create mode 100644 ktransformers/ktransformers/website/src/assets/css/mixins.styl
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/demo.css
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/demo_index.html
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.css
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.js
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.json
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.svg
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.ttf
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.woff
create mode 100644 ktransformers/ktransformers/website/src/assets/iconfont/iconfont.woff2
create mode 100644 ktransformers/ktransformers/website/src/components/chat/index.vue
create mode 100644 ktransformers/ktransformers/website/src/conf/config.ts
create mode 100644 ktransformers/ktransformers/website/src/locals/en.js
create mode 100644 ktransformers/ktransformers/website/src/locals/index.js
create mode 100644 ktransformers/ktransformers/website/src/locals/zh.js
create mode 100644 ktransformers/ktransformers/website/src/main.ts
create mode 100644 ktransformers/ktransformers/website/src/router/index.ts
create mode 100644 ktransformers/ktransformers/website/src/shims-vue.d.ts
create mode 100644 ktransformers/ktransformers/website/src/store/index.ts
create mode 100644 ktransformers/ktransformers/website/src/utils/copy.ts
create mode 100644 ktransformers/ktransformers/website/src/utils/types.ts
create mode 100644 ktransformers/ktransformers/website/src/views/home.vue
create mode 100644 ktransformers/ktransformers/website/tests/unit/example.spec.ts
create mode 100644 ktransformers/ktransformers/website/tsconfig.json
create mode 100644 ktransformers/ktransformers/website/vue.config.js
create mode 100644 ktransformers/ktransformers_ext/cpu_backend/.cpuinfer.h.swp
create mode 100644 ktransformers/optimize/.optimize.py.swp
create mode 100644 ktransformers/pyproject.toml
create mode 100644 ktransformers/requirements-local_chat.txt
create mode 100644 ktransformers/setup.py
diff --git a/ktransformers/.flake8 b/ktransformers/.flake8
new file mode 100644
index 00000000..fadb9882
--- /dev/null
+++ b/ktransformers/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+max-line-length = 120
+extend-select = B950
+extend-ignore = E203,E501,E701, B001,B006,B007,B008,B009,B010,B011,B016,B028,B031,B950,E265,E266,E401,E402,E711,E712,E713,E721,E722,E731,F401,F403,F405,F541,F811,F821,F841,W391
\ No newline at end of file
diff --git a/ktransformers/.github/workflows/package_wheel_release.yml b/ktransformers/.github/workflows/package_wheel_release.yml
new file mode 100644
index 00000000..dfbfde45
--- /dev/null
+++ b/ktransformers/.github/workflows/package_wheel_release.yml
@@ -0,0 +1,229 @@
+name: Build Wheels
+on:
+ workflow_dispatch:
+ inputs:
+ release:
+ description: 'Release? 1 = yes, 0 = no'
+ default: '0'
+ required: true
+ type: string
+jobs:
+ build_wheels:
+ name: ${{ matrix.os }} Python=${{ matrix.pyver }} CUDA=${{ matrix.cuda }} CPU_INSTRUCT=${{ matrix.instruct }} Torch=${{ matrix.torch }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # Ubuntu
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.11', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: ubuntu-20.04, pyver: '3.10', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+
+ # Windows
+ - { os: windows-2022, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.4.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.2.2', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.1.1', torch: '2.4.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX512', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.10', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.0;8.6;8.7;8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+
+ defaults:
+ run:
+ shell: pwsh
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Free Disk Space
+ uses: jlumbroso/free-disk-space@v1.3.1
+ if: runner.os == 'Linux'
+ with:
+ tool-cache: true
+ android: true
+ dotnet: true
+ haskell: true
+ large-packages: false
+ swap-storage: true
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.pyver }}
+
+ - name: check_space
+ run: |
+ if($IsLinux) {df -h}
+ if($IsWindows) {Get-PSDrive -PSProvider 'FileSystem'}
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Setup Mamba
+ if: matrix.cuda != ''
+ uses: conda-incubator/setup-miniconda@v3
+ with:
+ activate-environment: "ktransformers"
+ python-version: ${{ matrix.pyver }}
+ miniforge-variant: Miniforge3
+ miniforge-version: latest
+ use-mamba: true
+ add-pip-as-python-dependency: true
+ auto-activate-base: false
+
+
+
+ - name: build web
+ run: |
+ cd ktransformers/website/
+ npm install
+ npm run build
+ cd ../../
+
+ - name: build for cuda
+ if: matrix.cuda != ''
+ run: |
+ git submodule init
+ git submodule update
+ if($IsWindows){
+ $originalPath = Get-Location
+ Import-Module 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
+ Enter-VsDevShell -VsInstallPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' -DevCmdArguments '-arch=x64 -host_arch=x64'
+ $env:DISTUTILS_USE_SDK=1
+ Set-Location $originalPath
+ }
+ $cudaVersion = '${{ matrix.cuda }}'
+ $env:MAMBA_NO_LOW_SPEED_LIMIT = 1
+ mamba install -y -c nvidia/label/cuda-$cudaVersion cuda-toolkit cuda-runtime
+ $env:CUDA_PATH = $env:CONDA_PREFIX
+ $env:CUDA_HOME = $env:CONDA_PREFIX
+ if ($IsLinux) {
+ $env:LD_LIBRARY_PATH = $env:CONDA_PREFIX + '/lib:' + $env:LD_LIBRARY_PATH
+ $env:LD_LIBRARY_PATH = $env:CONDA_PREFIX + '/lib/python${{ matrix.pyver }}/site-packages/nvidia/nvjitlink/lib:' + $env:LD_LIBRARY_PATH
+ if (!(Test-Path $env:CUDA_HOME/lib64)) {
+ New-Item -ItemType SymbolicLink -Path $env:CUDA_HOME/lib64 -Target $env:CUDA_HOME/lib
+ }
+ }
+ if ($IsWindows) {
+ if (Test-Path -Path "$env:CUDA_PATH/Library/bin/nvcc.exe"){
+ $env:CUDA_PATH = "$env:CUDA_PATH/Library"
+ $env:CUDA_HOME = $env:CUDA_PATH
+ }
+ $env:PATH = "$env:CUDA_PATH/bin;" + $env:PATH
+ $directory = "$env:CUDA_PATH/lib/x64/"
+ if (-not (Test-Path -Path $directory)) {
+ New-Item -ItemType Directory -Path $directory
+ Write-Output "Directory '$directory' created."
+ }
+ cp $env:CUDA_PATH/lib/*.lib $env:CUDA_PATH/lib/x64/
+ $env:INCLUDE =$env:CUDA_PATH + "/include/targets/x64;" + $env:INCLUDE
+ $env:INCLUDE =$env:CONDA_PREFIX + "/include;" + $env:INCLUDE
+ }
+ python -m pip install torch==${{ matrix.torch }} torchvision torchaudio --index-url https://download.pytorch.org/whl/cu${{ matrix.torch_cu }}
+ python -m pip install cpufeature build wheel ninja packaging setuptools
+ $env:KTRANSFORMERS_FORCE_BUILD = "TRUE"
+ $env:CPU_INSTRUCT = '${{ matrix.instruct }}'
+ $env:TORCH_CUDA_ARCH_LIST = '${{ matrix.cudaarch }}'
+ python -m build --no-isolation --verbose
+
+
+ - name: create Rlease dir
+ run: |
+ if ($IsWindows) {
+ $env:date = $(Get-Date -Format "yyyy-MM-dd")
+ New-Item -ItemType Directory -Force -Path "$Env:USERPROFILE\.ssh"
+ $Env:SSH_PATH = "$Env:USERPROFILE\.ssh\id_rsa"
+ Set-Content -Path $Env:SSH_PATH -Value "${{ secrets.SSH_PRIVATE_KEY }}"
+ (Get-Content -Path $Env:SSH_PATH).Replace("`r`n","`n") | Set-Content -Path $Env:SSH_PATH
+ chmod 600 $Env:SSH_PATH
+ }
+ if ($IsLinux) {
+ $env:date = $(date +%Y-%m-%d)
+ mkdir -p ~/.ssh/
+ echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ }
+
+ ssh -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no root@${{ secrets.SSH_SERVER }} "mkdir -p /mnt/data/release-$env:date"
+ scp -P ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no dist/*.whl root@${{ secrets.SSH_SERVER }}:/mnt/data/release-$env:date/
\ No newline at end of file
diff --git a/ktransformers/.github/workflows/package_wheel_test.yml b/ktransformers/.github/workflows/package_wheel_test.yml
new file mode 100644
index 00000000..cd8db621
--- /dev/null
+++ b/ktransformers/.github/workflows/package_wheel_test.yml
@@ -0,0 +1,141 @@
+name: Build Wheels Tests
+on:
+ workflow_dispatch:
+ inputs:
+ release:
+ description: 'Release? 1 = yes, 0 = no'
+ default: '0'
+ required: true
+ type: string
+jobs:
+ build_wheels:
+ name: ${{ matrix.os }} Python=${{ matrix.pyver }} CUDA=${{ matrix.cuda }} CPU_INSTRUCT=${{ matrix.instruct }} Torch=${{ matrix.torch }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # Ubuntu
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '124'}
+ - { os: ubuntu-20.04, pyver: '3.12', cuda: '12.2.2', torch: '2.3.0', cudaarch: '8.9;9.0+PTX', instruct: 'FANCY', torch_cu: '121'}
+ - { os: windows-2022, pyver: '3.11', cuda: '12.5.1', torch: '2.4.0', cudaarch: '8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '124'}
+ - { os: windows-2022, pyver: '3.12', cuda: '12.1.1', torch: '2.3.0', cudaarch: '8.9;9.0+PTX', instruct: 'AVX2', torch_cu: '121'}
+
+ defaults:
+ run:
+ shell: pwsh
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Free Disk Space
+ uses: jlumbroso/free-disk-space@v1.3.1
+ if: runner.os == 'Linux'
+ with:
+ tool-cache: true
+ android: true
+ dotnet: true
+ haskell: true
+ large-packages: false
+ swap-storage: true
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.pyver }}
+
+ - name: check_space
+ run: |
+ if($IsLinux) {df -h}
+ if($IsWindows) {Get-PSDrive -PSProvider 'FileSystem'}
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Setup Mamba
+ if: matrix.cuda != ''
+ uses: conda-incubator/setup-miniconda@v3
+ with:
+ activate-environment: "ktransformers"
+ python-version: ${{ matrix.pyver }}
+ miniforge-variant: Miniforge3
+ miniforge-version: latest
+ use-mamba: true
+ add-pip-as-python-dependency: true
+ auto-activate-base: false
+
+
+
+ - name: build web
+ run: |
+ cd ktransformers/website/
+ npm install
+ npm run build
+ cd ../../
+
+ - name: build for cuda
+ if: matrix.cuda != ''
+ run: |
+ git submodule init
+ git submodule update
+ if($IsWindows){
+ $originalPath = Get-Location
+ Import-Module 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
+ Enter-VsDevShell -VsInstallPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' -DevCmdArguments '-arch=x64 -host_arch=x64'
+ $env:DISTUTILS_USE_SDK=1
+ Set-Location $originalPath
+ }
+ $cudaVersion = '${{ matrix.cuda }}'
+ $env:MAMBA_NO_LOW_SPEED_LIMIT = 1
+ mamba install -y -c nvidia/label/cuda-$cudaVersion cuda-toolkit cuda-runtime
+ $env:CUDA_PATH = $env:CONDA_PREFIX
+ $env:CUDA_HOME = $env:CONDA_PREFIX
+ if ($IsLinux) {
+ $env:LD_LIBRARY_PATH = $env:CONDA_PREFIX + '/lib:' + $env:LD_LIBRARY_PATH
+ $env:LD_LIBRARY_PATH = $env:CONDA_PREFIX + '/lib/python${{ matrix.pyver }}/site-packages/nvidia/nvjitlink/lib:' + $env:LD_LIBRARY_PATH
+ if (!(Test-Path $env:CUDA_HOME/lib64)) {
+ New-Item -ItemType SymbolicLink -Path $env:CUDA_HOME/lib64 -Target $env:CUDA_HOME/lib
+ }
+ }
+ if ($IsWindows) {
+ if (Test-Path -Path "$env:CUDA_PATH/Library/bin/nvcc.exe"){
+ $env:CUDA_PATH = "$env:CUDA_PATH/Library"
+ $env:CUDA_HOME = $env:CUDA_PATH
+ }
+ $env:PATH = "$env:CUDA_PATH/bin;" + $env:PATH
+ $directory = "$env:CUDA_PATH/lib/x64/"
+ if (-not (Test-Path -Path $directory)) {
+ New-Item -ItemType Directory -Path $directory
+ Write-Output "Directory '$directory' created."
+ }
+ cp $env:CUDA_PATH/lib/*.lib $env:CUDA_PATH/lib/x64/
+ $env:INCLUDE =$env:CUDA_PATH + "/include/targets/x64;" + $env:INCLUDE
+ $env:INCLUDE =$env:CONDA_PREFIX + "/include;" + $env:INCLUDE
+ }
+ python -m pip install torch==${{ matrix.torch }} torchvision torchaudio --index-url https://download.pytorch.org/whl/cu${{ matrix.torch_cu }}
+ python -m pip install cpufeature build wheel ninja packaging setuptools
+ $env:KTRANSFORMERS_FORCE_BUILD = "TRUE"
+ $env:CPU_INSTRUCT = '${{ matrix.instruct }}'
+ $env:TORCH_CUDA_ARCH_LIST = '${{ matrix.cudaarch }}'
+ python -m build --no-isolation --verbose
+
+
+ - name: create Rlease dir
+ run: |
+ if ($IsWindows) {
+ $env:date = $(Get-Date -Format "yyyy-MM-dd")
+ New-Item -ItemType Directory -Force -Path "$Env:USERPROFILE\.ssh"
+ $Env:SSH_PATH = "$Env:USERPROFILE\.ssh\id_rsa"
+ Set-Content -Path $Env:SSH_PATH -Value "${{ secrets.SSH_PRIVATE_KEY }}"
+ (Get-Content -Path $Env:SSH_PATH).Replace("`r`n","`n") | Set-Content -Path $Env:SSH_PATH
+ chmod 600 $Env:SSH_PATH
+ }
+ if ($IsLinux) {
+ $env:date = $(date +%Y-%m-%d)
+ mkdir -p ~/.ssh/
+ echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ }
+
+ ssh -p ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no root@${{ secrets.SSH_SERVER }} "mkdir -p /mnt/data/release-$env:date"
+ scp -P ${{ secrets.SSH_PORT }} -o StrictHostKeyChecking=no dist/*.whl root@${{ secrets.SSH_SERVER }}:/mnt/data/release-$env:date/
\ No newline at end of file
diff --git a/ktransformers/.gitignore b/ktransformers/.gitignore
new file mode 100644
index 00000000..d45e9564
--- /dev/null
+++ b/ktransformers/.gitignore
@@ -0,0 +1,24 @@
+__pycache__
+build
+.vscode
+*.so
+*.cache
+server.db
+logs
+node_modules
+*.nsys-rep
+.vs/
+*pycache*
+*build/
+*/third_party/*
+.DS_Store
+compile_commands.json
+*.egg-info*
+*dist/
+ktransformers/server/local_store/
+ktransformers/server_test1.db
+*.patch
+img/
+tmp1.txt
+test_65_300_1536.txt
+test.txt
diff --git a/ktransformers/.gitmodules b/ktransformers/.gitmodules
new file mode 100644
index 00000000..68242f3b
--- /dev/null
+++ b/ktransformers/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "third_party/llama.cpp"]
+ path = third_party/llama.cpp
+ url = https://github.com/ggerganov/llama.cpp.git
+[submodule "third_party/pybind11"]
+ path = third_party/pybind11
+ url = https://github.com/pybind/pybind11.git
diff --git a/ktransformers/.pylintrc b/ktransformers/.pylintrc
new file mode 100644
index 00000000..1ed4ddb3
--- /dev/null
+++ b/ktransformers/.pylintrc
@@ -0,0 +1,6 @@
+[MASTER]
+extension-pkg-whitelist=pydantic
+max-line-length=120
+
+[MESSAGES CONTROL]
+disable=missing-function-docstring
\ No newline at end of file
diff --git a/ktransformers/Dockerfile b/ktransformers/Dockerfile
new file mode 100644
index 00000000..d56a607f
--- /dev/null
+++ b/ktransformers/Dockerfile
@@ -0,0 +1,35 @@
+FROM node:20.16.0 as web_compile
+WORKDIR /home
+RUN <
+
+
+
+
+
+
+
+
+
+ A Flexible Framework for Experiencing Cutting-edge LLM Inference Optimizations
+ 🌟 Show Cases | 🚀 Quick Start | 📃 Tutorial | 💬 Discussion | 🙋 FAQ
+
+
+🎉 Introduction
+KTransformers, pronounced as Quick Transformers, is designed to enhance your 🤗 Transformers experience with advanced kernel optimizations and placement/parallelism strategies.
+
+KTransformers is a flexible, Python-centric framework designed with extensibility at its core.
+By implementing and injecting an optimized module with a single line of code, users gain access to a Transformers-compatible
+interface, RESTful APIs compliant with OpenAI and Ollama, and even a simplified ChatGPT-like web UI.
+
+Our vision for KTransformers is to serve as a flexible platform for experimenting with innovative LLM inference optimizations. Please let us know if you need any other features.
+
+🔥 Updates
+
+* **Feb 10, 2025**: Support Deepseek-R1 and V3 on single (24GB VRAM)/multi gpu and 382G DRAM, up to 3~28x speedup. The detailed tutorial is [here](./doc/en/DeepseekR1_V3_tutorial.md).
+* **Aug 28, 2024**: Support 1M context under the InternLM2.5-7B-Chat-1M model, utilizing 24GB of VRAM and 150GB of DRAM. The detailed tutorial is [here](./doc/en/long_context_tutorial.md).
+* **Aug 28, 2024**: Decrease DeepseekV2's required VRAM from 21G to 11G.
+* **Aug 15, 2024**: Update detailed [TUTORIAL](doc/en/injection_tutorial.md) for injection and multi-GPU.
+* **Aug 14, 2024**: Support llamfile as linear backend.
+* **Aug 12, 2024**: Support multiple GPU; Support new model: mixtral 8\*7B and 8\*22B; Support q2k, q3k, q5k dequant on gpu.
+* **Aug 9, 2024**: Support windows native.
+
+🌟 Show Cases
+
+
+
GPT-4/o1-level Local VSCode Copilot on a Desktop with only 24GB VRAM
+
+
+https://github.com/user-attachments/assets/ebd70bfa-b2c1-4abb-ae3b-296ed38aa285
+
+
+
+- **[NEW!!!] Local 671B DeepSeek-Coder-V3/R1:** Running its Q4_K_M version using only 14GB VRAM and 382GB DRAM([Tutorial](./doc/en/DeepseekR1_V3_tutorial.md)).
+ - Prefill Speed (tokens/s):
+ - KTransformers: 54.21 (32 cores) → 74.362 (dual-socket, 2×32 cores) → 255.26 (optimized AMX-based MoE kernel, V0.3 only) → 286.55 (selectively using 6 experts, V0.3 only)
+ - Compared to 10.31 tokens/s in llama.cpp with 2×32 cores, achieving up to **27.79× speedup**.
+ - Decode Speed (tokens/s):
+ - KTransformers: 8.73 (32 cores) → 11.26 (dual-socket, 2×32 cores) → 13.69 (selectively using 6 experts, V0.3 only)
+ - Compared to 4.51 tokens/s in llama.cpp with 2×32 cores, achieving up to **3.03× speedup**.
+ - Upcoming Open Source Release:
+ - AMX optimizations and selective expert activation will be open-sourced in V0.3.
+ - Currently available only in preview binary distribution, which can be downloaded [here](./doc/en/DeepseekR1_V3_tutorial.md).
+
+- **Local 236B DeepSeek-Coder-V2:** Running its Q4_K_M version using only 21GB VRAM and 136GB DRAM, attainable on a local desktop machine, which scores even better than GPT4-0613 in [BigCodeBench](https://huggingface.co/blog/leaderboard-bigcodebench).
+
+
+
+
+
+
+
+- **Faster Speed:** Achieving 126 tokens/s for 2K prompt prefill and 13.6 tokens/s for generation through MoE offloading and injecting advanced kernels from [Llamafile](https://github.com/Mozilla-Ocho/llamafile/tree/main) and [Marlin](https://github.com/IST-DASLab/marlin).
+- **VSCode Integration:** Wrapped into an OpenAI and Ollama compatible API for seamless integration as a backend for [Tabby](https://github.com/TabbyML/tabby) and various other frontends.
+
+
+
+https://github.com/user-attachments/assets/4c6a8a38-05aa-497d-8eb1-3a5b3918429c
+
+
+
+1M Context Local Inference on a Desktop with Only 24GB VRAM
+
+
+https://github.com/user-attachments/assets/a865e5e4-bca3-401e-94b8-af3c080e6c12
+
+* **1M Context InternLM 2.5 7B**: Operates at full bf16 precision, utilizing 24GB VRAM and 150GB DRAM, which is feasible on a local desktop setup. It achieves a 92.88% success rate on the 1M "Needle In a Haystack" test and 100% on the 128K NIAH test.
+
+
+
+
+
+
+
+
+
+
+
+
+
+* **Enhanced Speed**: Reaches 16.91 tokens/s for generation with a 1M context using sparse attention, powered by llamafile kernels. This method is over 10 times faster than full attention approach of llama.cpp.
+
+* **Flexible Sparse Attention Framework**: Offers a flexible block sparse attention framework for CPU offloaded decoding. Compatible with SnapKV, Quest, and InfLLm. Further information is available [here](./doc/en/long_context_introduction.md).
+
+
+
+More advanced features will coming soon, so stay tuned!
+
+🚀 Quick Start
+
+Preparation
+Some preparation:
+
+- CUDA 12.1 and above, if you didn't have it yet, you may install from [here](https://developer.nvidia.com/cuda-downloads).
+
+ ```sh
+ # Adding CUDA to PATH
+ export PATH=/usr/local/cuda/bin:$PATH
+ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
+ export CUDA_PATH=/usr/local/cuda
+ ```
+
+- Linux-x86_64 with gcc, g++ and cmake
+
+ ```sh
+ sudo apt-get update
+ sudo apt-get install gcc g++ cmake ninja-build
+ ```
+
+- We recommend using [Conda](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh) to create a virtual environment with Python=3.11 to run our program.
+
+ ```sh
+ conda create --name ktransformers python=3.11
+ conda activate ktransformers # you may need to run ‘conda init’ and reopen shell first
+ ```
+
+- Make sure that PyTorch, packaging, ninja is installed
+
+ ```
+ pip install torch packaging ninja cpufeature numpy
+ ```
+
+Installation
+
+1. Use a Docker image, see [documentation for Docker](./doc/en/Docker.md)
+
+2. You can install using Pypi (for linux):
+
+ ```
+ pip install ktransformers --no-build-isolation
+ ```
+
+ for windows we prepare a pre compiled whl package on [ktransformers-0.2.0+cu125torch24avx2-cp312-cp312-win_amd64.whl](https://github.com/kvcache-ai/ktransformers/releases/download/v0.2.0/ktransformers-0.2.0+cu125torch24avx2-cp312-cp312-win_amd64.whl), which require cuda-12.5, torch-2.4, python-3.11, more pre compiled package are being produced.
+
+3. Or you can download source code and compile:
+
+ - init source code
+
+ ```sh
+ git clone https://github.com/kvcache-ai/ktransformers.git
+ cd ktransformers
+ git submodule init
+ git submodule update
+ ```
+
+ - [Optional] If you want to run with website, please [compile the website](./doc/en/api/server/website.md) before execute ```bash install.sh```
+
+ - Compile and install (for Linux)
+
+ ```
+ bash install.sh
+ ```
+
+ - Compile and install(for Windows)
+
+ ```
+ install.bat
+ ```
+4. If you are developer, you can make use of the makefile to compile and format the code.
the detailed usage of makefile is [here](./doc/en/makefile_usage.md)
+Local Chat
+We provide a simple command-line local chat Python script that you can run for testing.
+
+> Note that this is a very simple test tool only support one round chat without any memory about last input, if you want to try full ability of the model, you may go to [RESTful API and Web UI](#id_666). We use the DeepSeek-V2-Lite-Chat-GGUF model as an example here. But we also support other models, you can replace it with any other model that you want to test.
+
+Run Example
+
+```shell
+# Begin from root of your cloned repo!
+# Begin from root of your cloned repo!!
+# Begin from root of your cloned repo!!!
+
+# Download mzwing/DeepSeek-V2-Lite-Chat-GGUF from huggingface
+mkdir DeepSeek-V2-Lite-Chat-GGUF
+cd DeepSeek-V2-Lite-Chat-GGUF
+
+wget https://huggingface.co/mzwing/DeepSeek-V2-Lite-Chat-GGUF/resolve/main/DeepSeek-V2-Lite-Chat.Q4_K_M.gguf -O DeepSeek-V2-Lite-Chat.Q4_K_M.gguf
+
+cd .. # Move to repo's root dir
+
+# Start local chat
+python -m ktransformers.local_chat --model_path deepseek-ai/DeepSeek-V2-Lite-Chat --gguf_path ./DeepSeek-V2-Lite-Chat-GGUF
+
+# If you see “OSError: We couldn't connect to 'https://huggingface.co' to load this file”, try:
+# GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/deepseek-ai/DeepSeek-V2-Lite
+# python ktransformers.local_chat --model_path ./DeepSeek-V2-Lite --gguf_path ./DeepSeek-V2-Lite-Chat-GGUF
+```
+
+It features the following arguments:
+
+- `--model_path` (required): Name of the model (such as "deepseek-ai/DeepSeek-V2-Lite-Chat" which will automatically download configs from [Hugging Face](https://huggingface.co/deepseek-ai/DeepSeek-V2-Lite)). Or if you already got local files you may directly use that path to initialize the model.
+
+ > Note: .safetensors files are not required in the directory. We only need config files to build model and tokenizer.
+
+- `--gguf_path` (required): Path of a directory containing GGUF files which could that can be downloaded from [Hugging Face](https://huggingface.co/mzwing/DeepSeek-V2-Lite-Chat-GGUF/tree/main). Note that the directory should only contains GGUF of current model, which means you need one separate directory for each model.
+
+- `--optimize_rule_path` (required except for Qwen2Moe and DeepSeek-V2): Path of YAML file containing optimize rules. There are two rule files pre-written in the [ktransformers/optimize/optimize_rules](ktransformers/optimize/optimize_rules) directory for optimizing DeepSeek-V2 and Qwen2-57B-A14, two SOTA MoE models.
+
+- `--max_new_tokens`: Int (default=1000). Maximum number of new tokens to generate.
+
+- `--cpu_infer`: Int (default=10). The number of CPUs used for inference. Should ideally be set to the (total number of cores - 2).
+
+ Suggested Model
+
+| Model Name | Model Size | VRAM | Minimum DRAM | Recommended DRAM |
+| ------------------------------ | ---------- | ----- | --------------- | ----------------- |
+| DeepSeek-R1-q4_k_m | 377G | 14G | 382G | 512G |
+| DeepSeek-V3-q4_k_m | 377G | 14G | 382G | 512G |
+| DeepSeek-V2-q4_k_m | 133G | 11G | 136G | 192G |
+| DeepSeek-V2.5-q4_k_m | 133G | 11G | 136G | 192G |
+| DeepSeek-V2.5-IQ4_XS | 117G | 10G | 107G | 128G |
+| Qwen2-57B-A14B-Instruct-q4_k_m | 33G | 8G | 34G | 64G |
+| DeepSeek-V2-Lite-q4_k_m | 9.7G | 3G | 13G | 16G |
+| Mixtral-8x7B-q4_k_m | 25G | 1.6G | 51G | 64G |
+| Mixtral-8x22B-q4_k_m | 80G | 4G | 86.1G | 96G |
+| InternLM2.5-7B-Chat-1M | 15.5G | 15.5G | 8G(32K context) | 150G (1M context) |
+
+
+More will come soon. Please let us know which models you are most interested in.
+
+Be aware that you need to be subject to their corresponding model licenses when using [DeepSeek](https://huggingface.co/deepseek-ai/DeepSeek-V2/blob/main/LICENSE) and [QWen](https://huggingface.co/Qwen/Qwen2-72B-Instruct/blob/main/LICENSE).
+
+
+ Click To Show how to run other examples
+
+* Qwen2-57B
+
+ ```sh
+ pip install flash_attn # For Qwen2
+
+ mkdir Qwen2-57B-GGUF && cd Qwen2-57B-GGUF
+
+ wget https://huggingface.co/Qwen/Qwen2-57B-A14B-Instruct-GGUF/resolve/main/qwen2-57b-a14b-instruct-q4_k_m.gguf?download=true -O qwen2-57b-a14b-instruct-q4_k_m.gguf
+
+ cd ..
+
+ python -m ktransformers.local_chat --model_name Qwen/Qwen2-57B-A14B-Instruct --gguf_path ./Qwen2-57B-GGUF
+
+ # If you see “OSError: We couldn't connect to 'https://huggingface.co' to load this file”, try:
+ # GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/Qwen/Qwen2-57B-A14B-Instruct
+ # python ktransformers/local_chat.py --model_path ./Qwen2-57B-A14B-Instruct --gguf_path ./DeepSeek-V2-Lite-Chat-GGUF
+ ```
+
+* DeepseekV2
+
+ ```sh
+ mkdir DeepSeek-V2-Chat-0628-GGUF && cd DeepSeek-V2-Chat-0628-GGUF
+ # Download weights
+ wget https://huggingface.co/bartowski/DeepSeek-V2-Chat-0628-GGUF/resolve/main/DeepSeek-V2-Chat-0628-Q4_K_M/DeepSeek-V2-Chat-0628-Q4_K_M-00001-of-00004.gguf -o DeepSeek-V2-Chat-0628-Q4_K_M-00001-of-00004.gguf
+ wget https://huggingface.co/bartowski/DeepSeek-V2-Chat-0628-GGUF/resolve/main/DeepSeek-V2-Chat-0628-Q4_K_M/DeepSeek-V2-Chat-0628-Q4_K_M-00002-of-00004.gguf -o DeepSeek-V2-Chat-0628-Q4_K_M-00002-of-00004.gguf
+ wget https://huggingface.co/bartowski/DeepSeek-V2-Chat-0628-GGUF/resolve/main/DeepSeek-V2-Chat-0628-Q4_K_M/DeepSeek-V2-Chat-0628-Q4_K_M-00003-of-00004.gguf -o DeepSeek-V2-Chat-0628-Q4_K_M-00003-of-00004.gguf
+ wget https://huggingface.co/bartowski/DeepSeek-V2-Chat-0628-GGUF/resolve/main/DeepSeek-V2-Chat-0628-Q4_K_M/DeepSeek-V2-Chat-0628-Q4_K_M-00004-of-00004.gguf -o DeepSeek-V2-Chat-0628-Q4_K_M-00004-of-00004.gguf
+
+ cd ..
+
+ python -m ktransformers.local_chat --model_name deepseek-ai/DeepSeek-V2-Chat-0628 --gguf_path ./DeepSeek-V2-Chat-0628-GGUF
+
+ # If you see “OSError: We couldn't connect to 'https://huggingface.co' to load this file”, try:
+
+ # GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/deepseek-ai/DeepSeek-V2-Chat-0628
+
+ # python -m ktransformers.local_chat --model_path ./DeepSeek-V2-Chat-0628 --gguf_path ./DeepSeek-V2-Chat-0628-GGUF
+ ```
+
+| model name | weights download link |
+|----------|----------|
+| Qwen2-57B | [Qwen2-57B-A14B-gguf-Q4K-M](https://huggingface.co/Qwen/Qwen2-57B-A14B-Instruct-GGUF/tree/main) |
+| DeepseekV2-coder |[DeepSeek-Coder-V2-Instruct-gguf-Q4K-M](https://huggingface.co/LoneStriker/DeepSeek-Coder-V2-Instruct-GGUF/tree/main) |
+| DeepseekV2-chat |[DeepSeek-V2-Chat-gguf-Q4K-M](https://huggingface.co/bullerwins/DeepSeek-V2-Chat-0628-GGUF/tree/main) |
+| DeepseekV2-lite | [DeepSeek-V2-Lite-Chat-GGUF-Q4K-M](https://huggingface.co/mzwing/DeepSeek-V2-Lite-Chat-GGUF/tree/main) |
+
+
+
+
+
+
+RESTful API and Web UI
+
+
+Start without website:
+
+```sh
+ktransformers --model_path deepseek-ai/DeepSeek-V2-Lite-Chat --gguf_path /path/to/DeepSeek-V2-Lite-Chat-GGUF --port 10002
+```
+
+Start with website:
+
+```sh
+ktransformers --model_path deepseek-ai/DeepSeek-V2-Lite-Chat --gguf_path /path/to/DeepSeek-V2-Lite-Chat-GGUF --port 10002 --web True
+```
+
+Or you want to start server with transformers, the model_path should include safetensors
+
+```bash
+ktransformers --type transformers --model_path /mnt/data/model/Qwen2-0.5B-Instruct --port 10002 --web True
+```
+
+Access website with url [http://localhost:10002/web/index.html#/chat](http://localhost:10002/web/index.html#/chat) :
+
+
+
+
+
+
+
+More information about the RESTful API server can be found [here](doc/en/api/server/server.md). You can also find an example of integrating with Tabby [here](doc/en/api/server/tabby.md).
+
+📃 Brief Injection Tutorial
+At the heart of KTransformers is a user-friendly, template-based injection framework.
+This allows researchers to easily replace original torch modules with optimized variants. It also simplifies the process of combining multiple optimizations, allowing the exploration of their synergistic effects.
+
+
+
+
+
+
+
+
+Given that vLLM already serves as a great framework for large-scale deployment optimizations, KTransformers is particularly focused on local deployments that are constrained by limited resources. We pay special attention to heterogeneous computing opportunities, such as GPU/CPU offloading of quantized models. For example, we support the efficient Llamafile and Marlin kernels for CPU and GPU, respectively. More details can be found here.
+
+Example Usage
+To utilize the provided kernels, users only need to create a YAML-based injection template and add the call to `optimize_and_load_gguf` before using the Transformers model.
+
+```python
+with torch.device("meta"):
+ model = AutoModelForCausalLM.from_config(config, trust_remote_code=True)
+optimize_and_load_gguf(model, optimize_rule_path, gguf_path, config)
+...
+generated = prefill_and_generate(model, tokenizer, input_tensor.cuda(), max_new_tokens=1000)
+```
+
+In this example, the AutoModel is first initialized on the meta device to avoid occupying any memory resources. Then, `optimize_and_load_gguf` iterates through all sub-modules of the model, matches rules specified in your YAML rule file, and replaces them with advanced modules as specified.
+
+After injection, the original `generate` interface is available, but we also provide a compatible `prefill_and_generate` method, which enables further optimizations like CUDAGraph to improve generation speed.
+
+How to custom your model
+
+A detailed tutorial of the injection and multi-GPU using DeepSeek-V2 as an example is given [here](doc/en/injection_tutorial.md).
+
+Below is an example of a YAML template for replacing all original Linear modules with Marlin, an advanced 4-bit quantization kernel.
+
+```yaml
+- match:
+ name: "^model\\.layers\\..*$" # regular expression
+ class: torch.nn.Linear # only match modules matching name and class simultaneously
+ replace:
+ class: ktransformers.operators.linear.KTransformerLinear # optimized Kernel on quantized data types
+ device: "cpu" # which devices to load this module when initializing
+ kwargs:
+ generate_device: "cuda"
+ generate_linear_type: "QuantizedLinearMarlin"
+```
+
+Each rule in the YAML file has two parts: `match` and `replace`. The `match` part specifies which module should be replaced, and the `replace` part specifies the module to be injected into the model along with the initialization keywords.
+
+You can find example rule templates for optimizing DeepSeek-V2 and Qwen2-57B-A14, two SOTA MoE models, in the [ktransformers/optimize/optimize_rules](ktransformers/optimize/optimize_rules) directory. These templates are used to power the `local_chat.py` demo.
+
+If you are interested in our design principles and the implementation of the injection framework, please refer to the [design document](doc/en/deepseek-v2-injection.md).
+
+Acknowledgment and Contributors
+
+The development of KTransformer is based on the flexible and versatile framework provided by Transformers. We also benefit from advanced kernels such as GGUF/GGML, Llamafile, and Marlin. We are planning to contribute back to the community by upstreaming our modifications.
+
+KTransformer is actively maintained and developed by contributors from the MADSys group at Tsinghua University and members from Approaching.AI. We welcome new contributors to join us in making KTransformer faster and easier to use.
+
+
+Discussion
+
+If you have any questions, feel free to open an issue. Alternatively, you can join our WeChat group for further discussion. QR Code: [WeChat Group](WeChatGroup.png)
+
+🙋 FAQ
+
+Some common questions are answered in the [FAQ](doc/en/FAQ.md).
diff --git a/ktransformers/WeChatGroup.png b/ktransformers/WeChatGroup.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ab99bd771a7fb987ac667f015b98e9ffb00b5ad
GIT binary patch
literal 849325
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
V;XU$b;n_H{Z4GTaQnokwZ$h-`pup+w@1r3
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 |