From 71801743c472818150268fb8c505d647e7fb98ad Mon Sep 17 00:00:00 2001 From: Concedo <39025047+LostRuins@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:24:30 +0800 Subject: [PATCH] added support for cohere north mini --- embd_res/klite.embd | 2 +- kcpp_adapters/AutoGuess.json | 13 ++++++++++- koboldcpp.py | 43 +++++++++++++++++++++++++++++------- src/llama-vocab.cpp | 16 ++++++-------- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/embd_res/klite.embd b/embd_res/klite.embd index c0b47f815..81d70a8fc 100644 --- a/embd_res/klite.embd +++ b/embd_res/klite.embd @@ -3562,7 +3562,7 @@ Current version indicated by LITEVER below. // whitelisted auto selected horde model names const defaultmodels = ["gpt4all","supercot","pygmalion-6","pygmalion-v8","pygmalion-2","hermes","airoboros","chrono","wizard","mantis","vicuna","manticore","alpaca","myth","xwin","spicyboros","mlewd","mxlewd","westlake","anubis","skyfall","llama2","llama3","llama-2","llama-3-","llama-3.","mistral","maid","mixtral","estopia","fighter","fimbul","euryale","nemo","gemma","lunaris","stheno","magnum","cydonia","qwen2.5-32b","behemoth","exaone","glm4","glm-4","tutu","deepseek","tlacuilo","rocinante","-14B","-32B","-27B","-35B"]; const ignoredmodels = ["tinyllama","debug-","-1b","-270m"]; //blacklisted model names - const hardcoded_think_closers = ["",""]; + const hardcoded_think_closers = ["","","","<|END_THINKING|>"]; const instructstartplaceholder = "\n{{[INPUT]}}\n"; const instructendplaceholder = "\n{{[OUTPUT]}}\n"; diff --git a/kcpp_adapters/AutoGuess.json b/kcpp_adapters/AutoGuess.json index 92a85a26f..de9a9ec94 100644 --- a/kcpp_adapters/AutoGuess.json +++ b/kcpp_adapters/AutoGuess.json @@ -201,7 +201,7 @@ } }, { "search": ["<|START_OF_TURN_TOKEN|>"], - "name": "Cohere (Aya Expanse 32B based)", + "name": "Cohere", "adapter": { "system_start": "<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>", "system_end": "<|END_OF_TURN_TOKEN|>", @@ -312,6 +312,17 @@ "assistant_end": "\n", "assistant_gen": "<|begin_assistant|>\nWe can respond now.\n[BEGIN FINAL RESPONSE]\n" } +}, { + "search": ["<|developer_end|>", "<|assistant_start|>","<|system_start|>"], + "name": "Apertus", + "adapter": { + "system_start": "<|system_start|>", + "system_end": "<|system_end|>", + "user_start": "<|user_start|>", + "user_end": "<|user_end|>", + "assistant_start": "<|assistant_start|>", + "assistant_end": "<|assistant_end|>" + } }, { "search": ["[e~[","]~b]user"], "name": "MiniMax", diff --git a/koboldcpp.py b/koboldcpp.py index e99e160fc..8f74ebb67 100644 --- a/koboldcpp.py +++ b/koboldcpp.py @@ -194,6 +194,7 @@ tool_call_pairs = [ #third element is optional str to match in chat template bef ("<|end|><|start|>assistant<|channel|>commentary to=", "", None, False), #gpt-oss ("<|tool_call_start|>", "<|tool_call_end|>", "CONTINUE_FINAL_MESSAGE_TAG", True), #lfm2.5 ("", "", "[BEGIN FINAL RESPONSE]", True), #apriel + ("<|START_ACTION|>", "<|END_ACTION|>", "<|START_OF_TURN_TOKEN|>", True), #cohere ] deprecated_keys = { "hordeconfig", @@ -3578,21 +3579,47 @@ def toolcall_to_normalized_json(text,start_tag,end_tag,required_match_txt): #con except Exception: return text + def parse_cohere2moe(text: str) -> str: + text = text.strip() + try: + calls = json.loads(text) + except Exception: + return text + if isinstance(calls, dict): + calls = [calls] + if not isinstance(calls, list): + return text + results = [] + for call in calls: + if not isinstance(call, dict): + continue + fn_name = call.get("tool_name") or call.get("name") + args = call.get("parameters", call.get("arguments", {})) + if not fn_name: + continue + results.append({"name": fn_name,"arguments": args if isinstance(args, dict) else {}}) + if not results: + return text + return json.dumps(results) if len(results) > 1 else json.dumps(results[0]) + # gemma4 takes precedence, since it can contain valid json fragments if end_tag=="": return parse_gemma4(text) + if start_tag=="<|tool_call_start|>" and end_tag=="<|tool_call_end|>" and cached_chat_template and (required_match_txt is None or required_match_txt in cached_chat_template): + return parse_lfm25(text) + + if start_tag=="<|START_ACTION|>" and end_tag=="<|END_ACTION|>" and cached_chat_template and (required_match_txt is None or required_match_txt in cached_chat_template): + return parse_cohere2moe(text) + + if start_tag=="" and end_tag=="" and cached_chat_template and (required_match_txt is None or required_match_txt in cached_chat_template): + return extract_json_from_string(text, True) #apriel is json + #if we are already valid JSON, return check_ok = extract_json_from_string(text, True) if check_ok and len(check_ok)>0: return text #is valid JSON or parsable - if start_tag=="<|tool_call_start|>" and end_tag=="<|tool_call_end|>" and cached_chat_template and (required_match_txt is None or required_match_txt in cached_chat_template): - return parse_lfm25(text) - - if start_tag=="" and end_tag=="" and cached_chat_template and (required_match_txt is None or required_match_txt in cached_chat_template): - return extract_json_from_string(text, True) #apriel is json - if "" in text and "" in text: # handle glm with args return parse_glm(text) @@ -3655,14 +3682,14 @@ def format_jinja(messages_orig, tools, chat_template_kwargs=None): print(f"Warning: Jinja template raised an exception: {msg}") return "" global cached_chat_template - from jinja2.ext import Extension + from jinja2.ext import Extension, loopcontrols class IgnoreGenerationTags(Extension): tags = {"generation"} def parse(self, parser): parser.stream.skip(1) return parser.parse_statements( ("name:endgeneration",), drop_needle=True) from jinja2.sandbox import ImmutableSandboxedEnvironment - jinja_env = ImmutableSandboxedEnvironment(trim_blocks=True, lstrip_blocks=True, extensions=[IgnoreGenerationTags]) + jinja_env = ImmutableSandboxedEnvironment(trim_blocks=True, lstrip_blocks=True, extensions=[IgnoreGenerationTags,loopcontrols]) # sanitize messages to remove none types messages = json.loads(json.dumps(messages_orig)) for m in messages: diff --git a/src/llama-vocab.cpp b/src/llama-vocab.cpp index 4c4047d17..c6eb2ed2c 100644 --- a/src/llama-vocab.cpp +++ b/src/llama-vocab.cpp @@ -3052,20 +3052,18 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) { attr = LLAMA_TOKEN_ATTR_USER_DEFINED; } - //kcpp hack render these - if (t.first == "<|tool_call_start|>" || t.first == "<|tool_call_end|>") { + //kcpp hack: render these special tokens + if (t.first == "<|tool_call_start|>" || t.first == "<|tool_call_end|>" + || t.first=="<|START_THINKING|>" || t.first=="<|END_THINKING|>" + || t.first=="<|START_ACTION|>" || t.first=="<|END_ACTION|>" + || t.first == "[THINK]" || t.first == "[/THINK]" + || t.first == "" || t.first == "" + || t.first == "[CALL_ID]" || t.first == "[TOOL_CONTENT]" || t.first == "[TOOL_CALLS]" || t.first == "[ARGS]") { LLAMA_LOG_WARN("%s: setting token '%s' (%d) attribute to USER_DEFINED (%u), old attributes: %u\n", __func__, t.first.c_str(), t.second, LLAMA_TOKEN_ATTR_USER_DEFINED, attr); - attr = LLAMA_TOKEN_ATTR_USER_DEFINED; } - if (t.first == "[THINK]" || t.first == "[/THINK]" || t.first == "" || t.first == "" || - t.first == "[CALL_ID]" || t.first == "[TOOL_CONTENT]" || t.first == "[TOOL_CALLS]" || t.first == "[ARGS]") { - LLAMA_LOG_WARN("%s: setting token '%s' (%d) attribute to USER_DEFINED (%u), old attributes: %u\n", - __func__, t.first.c_str(), t.second, LLAMA_TOKEN_ATTR_USER_DEFINED, attr); - attr = LLAMA_TOKEN_ATTR_USER_DEFINED; - } } // sanity checks