From 5a3369fd2a6746905dc177b1cddfd7c91550aa80 Mon Sep 17 00:00:00 2001 From: Concedo <39025047+LostRuins@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:13:51 +0800 Subject: [PATCH] support for gpt oss jinja --- embd_res/klite.embd | 8 +++++++- koboldcpp.py | 45 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/embd_res/klite.embd b/embd_res/klite.embd index c826ce288..d7e84c5f3 100644 --- a/embd_res/klite.embd +++ b/embd_res/klite.embd @@ -8448,7 +8448,13 @@ Current version indicated by LITEVER below. } else { - gametext_arr.push(get_instructendplaceholder() + callresp); + let callresptxt = ""; + if(!(gametext_arr.length>0 && gametext_arr[gametext_arr.length-1].trim().endsWith("{{[OUTPUT]}}"))) + { + callresptxt = get_instructendplaceholder(); //add instruct tag if needed + } + callresptxt += JSON.stringify(callresp) + get_instructendplaceholder(); + gametext_arr.push(callresptxt); } } function parseMcpUrls() { diff --git a/koboldcpp.py b/koboldcpp.py index 7c341b201..aafe0f6dd 100755 --- a/koboldcpp.py +++ b/koboldcpp.py @@ -169,13 +169,14 @@ zenity_permitted = True thinkformats = [{"start":"<|channel|>analysis<|message|>","end":"<|start|>assistant<|channel|>final<|message|>"}, {"start":"","end":""}, {"start":"<|channel>thought","end":""}] -tool_call_pairs = [ - ("", ""), - ("", ""), - ("<|tool_call_begin|>", "<|tool_call_end|>"), - ("<|tool▁call▁begin|>", "<|tool▁call▁end|>"), - ("", ""), - ("<|tool_call>", ""), +tool_call_pairs = [ #third element is whether its stream-handleable + ("", "", True), + ("", "", True), + ("<|tool_call_begin|>", "<|tool_call_end|>", True), + ("<|tool▁call▁begin|>", "<|tool▁call▁end|>", True), + ("", "", True), + ("<|tool_call>", "", True), + ("<|end|><|start|>assistant<|channel|>commentary to=", "", False), ] saved_stdout = None @@ -3254,6 +3255,21 @@ def toolcall_to_normalized_json(text,start_tag,end_tag): #convert weird formats pass return text + def parse_gpt_oss(text: str) -> str: + fn_match = re.search(r'functions\.([a-zA-Z_][a-zA-Z0-9_]*)', text) + if not fn_match: + return text + fn_name = fn_match.group(1).strip() + msg_split = text.split('<|message|>', 1) + if len(msg_split) < 2: + return text + args_block = msg_split[1].strip() + try: + args = json.loads(args_block) + except Exception: + return text + return json.dumps({"name": fn_name, "arguments": args}) + # gemma4 takes precedence, since it can contain valid json fragments if end_tag=="": return parse_gemma4(text) @@ -3278,6 +3294,9 @@ def toolcall_to_normalized_json(text,start_tag,end_tag): #convert weird formats if ' ' not in text and '\n' not in text: # handle glm without args return parse_glm(text) + if 'functions.' in text and "commentary" in start_tag: # handle GPT-OSS + return parse_gpt_oss(text) + return text #fallback def repack_toolcall_tags(text: str, original_tools:list): @@ -3290,8 +3309,12 @@ def repack_toolcall_tags(text: str, original_tools:list): text = re.sub(pattern, '', text, flags=re.DOTALL) text = text.strip() found = False - for start, end in tool_call_pairs: - pattern = re.escape(start) + r"(.*?)" + re.escape(end) + for start, end, streamhandled in tool_call_pairs: + pattern="" + if end: + pattern = re.escape(start) + r"(.*?)" + re.escape(end) + else: + pattern = re.escape(start) + r"(.*)$" # match to end of string matches = re.findall(pattern, text, flags=re.DOTALL) if matches: found = True @@ -4747,8 +4770,8 @@ class KcppServerRequestHandler(http.server.SimpleHTTPRequestHandler): # if tools, do not send anything else - OAI tool calls will be handled with fakestreaming! # only exception is if we know the exact toolcall tag to segment! tool_segment_tag = "" - for start, end in tool_call_pairs: - if cached_chat_template and start in cached_chat_template: + for start, end, streamhandled in tool_call_pairs: + if streamhandled and cached_chat_template and start in cached_chat_template: tool_segment_tag = start break jinjatools = (args.jinja and args.jinja_tools)