@@ -312,42 +312,207 @@ def save_model_gguf(self):
312312
313313 def prepare_modelfile_content (self ):
314314 output_model = self .config ["hf_model_name" ]
315- # Determine stop tokens from config or infer based on model name
316315 model_name = self .config ["model_name" ].lower ()
317- if "phi" in model_name :
318- inferred_stop_tokens = ["<|end|>" , "<|user|>" , "<|assistant|>" ]
319- elif "llava" in model_name :
320- inferred_stop_tokens = ["</s>" , "USER:" , "ASSSISTANT:" ]
321- elif "mistral" in model_name :
322- inferred_stop_tokens = ["[INST]" , "[/INST]" ]
323- elif "qwen" in model_name :
324- inferred_stop_tokens = ["<|endoftext|>" ]
325- elif "deepseek" in model_name :
326- inferred_stop_tokens = ["<|begin▁of▁sentence|>" , "<|end▁of▁sentence|>" , "<|User|>" , "<|Assistant|>" ]
327- else :
328- inferred_stop_tokens = ["<|start_header_id|>" , "<|end_header_id|>" , "<|eot_id|>" ]
329- # Use stop_tokens from config if provided, otherwise use inferred
330- model_stop_tokens = self .config .get ("stop_tokens" , inferred_stop_tokens )
331-
332- gguf_path = f"{ output_model } /unsloth.Q4_K_M.gguf"
333- if not os .path .exists (gguf_path ):
334- self .model , self .hf_tokenizer = self .load_model ()
335- self .save_model_gguf ()
336- stop_parameters = "\n " .join ([f'PARAMETER stop "{ token } "' for token in model_stop_tokens ])
337- return f"""FROM { output_model } /unsloth.Q4_K_M.gguf
338-
339- TEMPLATE \" \" \" Below are some instructions that describe some tasks. Write responses that appropriately complete each request.{{{{ if .Prompt }}}}
340-
341- ### Instruction:
342- {{{{ .Prompt }}}}
343-
344- {{{{ end }}}}### Response:
345- {{{{ .Response }}}}\" \" \"
346-
347- { stop_parameters }
316+ # Mapping from model name keywords to their default TEMPLATE and stop tokens (and optional SYSTEM/num_ctx)
317+ mapping = {
318+ "llama" : {
319+ "template" : """<|start_header_id|>system<|end_header_id|>
320+ Cutting Knowledge Date: December 2023
321+ {{ if .System }}{{ .System }}
322+ {{- end }}
323+ {{- if .Tools }}When you receive a tool call response, use the output to format an answer to the orginal user question.
324+ You are a helpful assistant with tool calling capabilities.
325+ {{- end }}<|eot_id|>
326+ {{- range $i, $_ := .Messages }}
327+ {{- $last := eq (len (slice $.Messages $i)) 1 }}
328+ {{- if eq .Role "user" }}<|start_header_id|>user<|end_header_id|>
329+ {{- if and $.Tools $last }}
330+ Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.
331+ Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
332+ {{ range $.Tools }}
333+ {{- . }}
334+ {{ end }}
335+ {{ .Content }}<|eot_id|>
336+ {{- else }}
337+ {{ .Content }}<|eot_id|>
338+ {{- end }}{{ if $last }}<|start_header_id|>assistant<|end_header_id|>
339+ {{ end }}
340+ {{- else if eq .Role "assistant" }}<|start_header_id|>assistant<|end_header_id|>
341+ {{- if .ToolCalls }}
342+ {{ range .ToolCalls }}
343+ {"name": "{{ .Function.Name }}", "parameters": {{ .Function.Arguments }}}{{ end }}
344+ {{- else }}
345+ {{ .Content }}
346+ {{- end }}{{ if not $last }}<|eot_id|>{{ end }}
347+ {{- else if eq .Role "tool" }}<|start_header_id|>ipython<|end_header_id|>
348+ {{ .Content }}<|eot_id|>{{ if $last }}<|start_header_id|>assistant<|end_header_id|>
349+ {{ end }}
350+ {{- end }}
351+ {{- end }}""" ,
352+ "stop_tokens" : ["<|start_header_id|>" , "<|end_header_id|>" , "<|eot_id|>" ]
353+ },
354+ "qwen" : {
355+ "template" : """{{- if .Suffix }}<|fim_prefix|>{{ .Prompt }}<|fim_suffix|>{{ .Suffix }}<|fim_middle|>
356+ {{- else if .Messages }}
357+ {{- if or .System .Tools }}<|im_start|>system
358+ {{- if .System }}
359+ {{ .System }}
360+ {{- end }}
361+ {{- if .Tools }}
362+ # Tools
363+ You may call one or more functions to assist with the user query.
364+ You are provided with function signatures within <tools></tools> XML tags:
365+ <tools>
366+ {{- range .Tools }}
367+ {"type": "function", "function": {{ .Function }}}
368+ {{- end }}
369+ </tools>
370+ For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
371+ <tool_call>
372+ {"name": <function-name>, "arguments": <args-json-object>}
373+ </tool_call>
374+ {{- end }}<|im_end|>
375+ {{ end }}
376+ {{- range $i, $_ := .Messages }}
377+ {{- $last := eq (len (slice $.Messages $i)) 1 -}}
378+ {{- if eq .Role "user" }}<|im_start|>user
379+ {{ .Content }}<|im_end|>
380+ {{ else if eq .Role "assistant" }}<|im_start|>assistant
381+ {{ if .Content }}{{ .Content }}
382+ {{- else if .ToolCalls }}<tool_call>
383+ {{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
384+ {{ end }}</tool_call>
385+ {{- end }}{{ if not $last }}<|im_end|>
386+ {{ end }}
387+ {{- else if eq .Role "tool" }}<|im_start|>user
388+ <tool_response>
389+ {{ .Content }}
390+ </tool_response><|im_end|>
391+ {{ end }}
392+ {{- if and (ne .Role "assistant") $last }}<|im_start|>assistant
393+ {{ end }}
394+ {{- end }}
395+ {{- else }}
396+ {{- if .System }}<|im_start|>system
397+ {{ .System }}<|im_end|>
398+ {{ end }}{{ if .Prompt }}<|im_start|>user
399+ {{ .Prompt }}<|im_end|>
400+ {{ end }}<|im_start|>assistant
401+ {{ end }}{{ .Response }}{{ if .Response }}<|im_end|>{{ end }}""" ,
402+ "system" : "You are Qwen, created by Alibaba Cloud. You are a helpful assistant." ,
403+ "num_ctx" : 32768 ,
404+ "stop_tokens" : ["<|endoftext|>" ]
405+ },
406+ "mistral" : {
407+ "template" : "[INST] {{ if .System }}{{ .System }} {{ end }}{{ .Prompt }} [/INST]" ,
408+ "stop_tokens" : ["[INST]" , "[/INST]" ]
409+ },
410+ "phi" : {
411+ "template" : """{{- range $i, $_ := .Messages }}
412+ {{- $last := eq (len (slice $.Messages $i)) 1 -}}
413+ <|im_start|>{{ .Role }}<|im_sep|>
414+ {{ .Content }}{{ if not $last }}<|im_end|>
415+ {{ end }}
416+ {{- if and (ne .Role "assistant") $last }}<|im_end|>
417+ <|im_start|>assistant<|im_sep|>
418+ {{ end }}
419+ {{- end }}""" ,
420+ "stop_tokens" : ["<|im_start|>" , "<|im_end|>" , "<|im_sep|>" ]
421+ },
422+ "deepseek" : {
423+ "template" : """{{- if .System }}{{ .System }}{{ end }}
424+ {{- range $i, $_ := .Messages }}
425+ {{- $last := eq (len (slice $.Messages $i)) 1}}
426+ {{- if eq .Role "user" }}<|User|>{{ .Content }}
427+ {{- else if eq .Role "assistant" }}<|Assistant|>{{ .Content }}{{- if not $last }}<|end▁of▁sentence|>{{- end }}
428+ {{- end }}
429+ {{- if and $last (ne .Role "assistant") }}<|Assistant|>{{- end }}
430+ {{- end }}""" ,
431+ "stop_tokens" : ["<|begin▁of▁sentence|>" , "<|end▁of▁sentence|>" , "<|User|>" , "<|Assistant|>" ]
432+ },
433+ "llava" : {
434+ "template" : """{{- if .Suffix }}<|fim_prefix|>{{ .Prompt }}<|fim_suffix|>{{ .Suffix }}<|fim_middle|>
435+ {{- else if .Messages }}
436+ {{- if or .System .Tools }}<|im_start|>system
437+ {{- if .System }}
438+ {{ .System }}
439+ {{- end }}
440+ {{- if .Tools }}
441+ # Tools
442+ You may call one or more functions to assist with the user query.
443+ You are provided with function signatures within <tools></tools> XML tags:
444+ <tools>
445+ {{- range .Tools }}
446+ {"type": "function", "function": {{ .Function }}}
447+ {{- end }}
448+ </tools>
449+ For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
450+ <tool_call>
451+ {"name": <function-name>, "arguments": <args-json-object>}
452+ </tool_call>
453+ {{- end }}<|im_end|>
454+ {{ end }}
455+ {{- range $i, $_ := .Messages }}
456+ {{- $last := eq (len (slice $.Messages $i)) 1 -}}
457+ {{- if eq .Role "user" }}<|im_start|>user
458+ {{ .Content }}<|im_end|>
459+ {{ else if eq .Role "assistant" }}<|im_start|>assistant
460+ {{ if .Content }}{{ .Content }}
461+ {{- else if .ToolCalls }}<tool_call>
462+ {{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
463+ {{ end }}</tool_call>
464+ {{- end }}{{ if not $last }}<|im_end|>
465+ {{ end }}
466+ {{- else if eq .Role "tool" }}<|im_start|>user
467+ <tool_response>
468+ {{ .Content }}
469+ </tool_response><|im_end|>
470+ {{ end }}
471+ {{- if and (ne .Role "assistant") $last }}<|im_start|>assistant
472+ {{ end }}
473+ {{- end }}
474+ {{- else }}
475+ {{- if .System }}<|im_start|>system
476+ {{ .System }}<|im_end|>
477+ {{ end }}{{ if .Prompt }}<|im_start|>user
478+ {{ .Prompt }}<|im_end|>
479+ {{ end }}<|im_start|>assistant
480+ {{ end }}{{ .Response }}{{ if .Response }}<|im_end|>{{ end }}""" ,
481+ "stop_tokens" : ["</s>" , "USER:" , "ASSSISTANT:" ]
482+ }
483+ }
484+ # Select mapping by checking if any key is in the model_name.
485+ chosen = None
486+ for key , settings in mapping .items ():
487+ if key in model_name :
488+ chosen = settings
489+ break
490+ if chosen is None :
491+ # Fallback default
492+ chosen = {
493+ "template" : """{{ if .System }}<|start_header_id|>system<|end_header_id|>
494+ {{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
495+ {{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
496+ {{ .Response }}<|eot_id|>""" ,
497+ "stop_tokens" : ["<|start_header_id|>" , "<|end_header_id|>" , "<|eot_id|>" ]
498+ }
499+ # Build the stop parameter lines.
500+ stop_params = "\n " .join ([f"PARAMETER stop { token } " for token in chosen ["stop_tokens" ]])
501+ # Optionally include a SYSTEM line and num_ctx if defined in the mapping.
502+ system_line = ""
503+ if "system" in chosen :
504+ system_line = f"SYSTEM { chosen ['system' ]} \n "
505+ num_ctx_line = ""
506+ if "num_ctx" in chosen :
507+ num_ctx_line = f"PARAMETER num_ctx { chosen ['num_ctx' ]} \n "
508+ # Assemble and return the modelfile content.
509+ return f"""FROM { output_model }
510+ TEMPLATE \" \" \" { chosen ['template' ]} \" \" \"
511+ { system_line } { num_ctx_line } { stop_params }
348512 """
349513
350514
515+
351516 def create_and_push_ollama_model (self ):
352517 modelfile_content = self .prepare_modelfile_content ()
353518 with open ("Modelfile" , "w" ) as file :
0 commit comments