diff --git a/convert_hf_to_gguf.py b/convert_hf_to_gguf.py index 018a2a588..b5a4a4aaa 100755 --- a/convert_hf_to_gguf.py +++ b/convert_hf_to_gguf.py @@ -699,6 +699,9 @@ class Model: if chkhsh == "b3f499bb4255f8ca19fccd664443283318f2fd2414d5e0b040fbdd0cc195d6c5": # ref: https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B res = "deepseek-r1-qwen" + if chkhsh == "a3df2b8943e01cfd7d68c9f8446b294f3d8706d1d6853df65df7fda5d4fcb19f": + # ref: https://huggingface.co/Almawave/Velvet-14B + res = "velvet" if res is None: logger.warning("\n") diff --git a/convert_hf_to_gguf_update.py b/convert_hf_to_gguf_update.py index cea34413f..241d04557 100755 --- a/convert_hf_to_gguf_update.py +++ b/convert_hf_to_gguf_update.py @@ -109,6 +109,7 @@ models = [ {"name": "megrez", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Infinigence/Megrez-3B-Instruct"}, {"name": "deepseek-v3", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/deepseek-ai/DeepSeek-V3"}, {"name": "deepseek-r1-qwen", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B"}, + {"name": "velvet", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Almawave/Velvet-14B"} ] diff --git a/include/llama.h b/include/llama.h index 3784f7d39..5d92e512c 100644 --- a/include/llama.h +++ b/include/llama.h @@ -105,6 +105,7 @@ extern "C" { LLAMA_VOCAB_PRE_TYPE_CHAMELEON = 26, LLAMA_VOCAB_PRE_TYPE_MINERVA = 27, LLAMA_VOCAB_PRE_TYPE_DEEPSEEK3_LLM = 28, + LLAMA_VOCAB_PRE_TYPE_VELVET = 29, }; enum llama_rope_type { diff --git a/src/llama-chat.cpp b/src/llama-chat.cpp index 028a64794..af0539bd4 100644 --- a/src/llama-chat.cpp +++ b/src/llama-chat.cpp @@ -58,6 +58,7 @@ static const std::map LLM_CHAT_TEMPLATES = { { "granite", LLM_CHAT_TEMPLATE_GRANITE }, { "gigachat", LLM_CHAT_TEMPLATE_GIGACHAT }, { "megrez", LLM_CHAT_TEMPLATE_MEGREZ }, + { "velvet", LLM_CHAT_TEMPLATE_VELVET }, }; llm_chat_template llm_chat_template_from_str(const std::string & name) { @@ -167,6 +168,8 @@ llm_chat_template llm_chat_detect_template(const std::string & tmpl) { return LLM_CHAT_TEMPLATE_GIGACHAT; } else if (tmpl_contains("<|role_start|>")) { return LLM_CHAT_TEMPLATE_MEGREZ; + } else if (tmpl_contains("")) { + return LLM_CHAT_TEMPLATE_VELVET; } return LLM_CHAT_TEMPLATE_UNKNOWN; } @@ -566,6 +569,31 @@ int32_t llm_chat_apply_template( if (add_ass) { ss << "<|role_start|>assistant<|role_end|>"; } + } else if (tmpl == LLM_CHAT_TEMPLATE_VELVET) { + // Velvet template + std::string leading_space = ""; + std::string trailing_space = ""; + bool trim_assistant_message = false; + bool is_inside_turn = false; + std::string system_message = ""; + std::string last_message(chat.back()->content); + ss << ""; + for (auto message : chat) { + if (!is_inside_turn) { + ss << leading_space << "" << trailing_space; + is_inside_turn = true; + } + std::string role(message->role); + std::string content(message->content); + if (role == "system") { + system_message = content + "\n\n"; + } else if (role == "user") { + ss << (content==last_message ? system_message : "") << content << leading_space << ""; + } else { + ss << trailing_space << (trim_assistant_message ? trim(content) : content) << ""; + is_inside_turn = false; + } + } } else { // template not supported return -1; diff --git a/src/llama-chat.h b/src/llama-chat.h index 2f6a0e3e2..0fe4b8e22 100644 --- a/src/llama-chat.h +++ b/src/llama-chat.h @@ -38,6 +38,7 @@ enum llm_chat_template { LLM_CHAT_TEMPLATE_GRANITE, LLM_CHAT_TEMPLATE_GIGACHAT, LLM_CHAT_TEMPLATE_MEGREZ, + LLM_CHAT_TEMPLATE_VELVET, LLM_CHAT_TEMPLATE_UNKNOWN, }; diff --git a/tests/test-chat-template.cpp b/tests/test-chat-template.cpp index e0314ae1d..d970beb57 100644 --- a/tests/test-chat-template.cpp +++ b/tests/test-chat-template.cpp @@ -262,6 +262,14 @@ int main(void) { /* .bos_token= */ "", /* .eos_token= */ "", }, + { + /* .name= */ "velvet", + /* .template_str= */ "{%- if messages[0][\"role\"] == \"system\" %}\n {%- set system_message = messages[0][\"content\"] %}\n {%- set loop_messages = messages[1:] %}\n{%- else %}\n {%- set loop_messages = messages %}\n{%- endif %}\n{%- if not tools is defined %}\n {%- set tools = none %}\n{%- endif %}\n{%- set user_messages = loop_messages | selectattr(\"role\", \"equalto\", \"user\") | list %}\n\n{#- This block checks for alternating user/assistant messages, skipping tool calling messages #}\n{%- set ns = namespace() %}\n{%- set ns.index = 0 %}\n{%- for message in loop_messages %}\n {%- if not (message.role == \"tool\" or message.role == \"tool_results\" or (message.tool_calls is defined and message.tool_calls is not none)) %}\n {%- if (message[\"role\"] == \"user\") != (ns.index % 2 == 0) %}\n {{- raise_exception(\"After the optional system message, conversation roles must alternate user/assistant/user/assistant/...\") }}\n {%- endif %}\n {%- set ns.index = ns.index + 1 %}\n {%- endif %}\n{%- endfor %}\n\n{{- bos_token }}\n{%- for message in loop_messages %}\n {%- if message[\"role\"] == \"user\" %}\n {%- if message == user_messages[-1] and system_message is defined and tools is not none %}\n {{- \"\" + system_message + \"\\n\\n\" + \"[\" }}\n {%- for tool in tools %}\n{{ tool }}\n {%- if not loop.last %}\n {{- \", \" }}\n {%- else %}\n {{- \"]\" }}\n {%- endif %}\n {%- endfor %}\n {{- \"\" + \"\\n\\n\" + message[\"content\"] + \"\" }}\n {%- elif message == user_messages[-1] and system_message is defined and tools is none %}\n {{- \"\" + system_message + \"\\n\\n\" + message[\"content\"] + \"\" }}\n {%- else %}\n {{- \"\" + message[\"content\"] + \"\" }}\n {%- endif %}\n {%- elif (message.tool_calls is defined and message.tool_calls is not none) %}\n {{- \"[\" }}\n {%- for tool_call in message.tool_calls %}\n{{ tool_call }}\n {%- if not loop.last %}\n {{- \", \" }}\n {%- else %}\n {{- \"]\" }}\n {%- endif %}\n {%- endfor %} {{- \"\" + eos_token }}\n {%- elif message[\"role\"] == \"assistant\" %}\n {{- message[\"content\"] + eos_token}}\n {%- elif message[\"role\"] == \"tool_results\" or message[\"role\"] == \"tool\" %}\n {%- if message.content is defined and message.content.content is defined %}\n {%- set content = message.content.content %}\n {%- else %}\n {%- set content = message.content %}\n {%- endif %}\n {{- '{\"content\": ' + content|string }}\n {{- '}' }}\n {%- else %}\n {{- raise_exception(\"Only user and assistant roles are supported, with the exception of an initial optional system message!\") }}\n {%- endif %}\n{%- endfor %}\n", + /* .expected_output= */ "HelloHi thereWho are you I am an assistant You are a helpful assistant\n\nAnother question", + /* .expected_output_jinja= */ "HelloHi thereWho are you I am an assistant You are a helpful assistant\n\nAnother question", + /* .bos_token= */ "", + /* .eos_token= */ "", + }, }; std::vector formatted_chat(1024); int32_t res;