mirror of
https://github.com/ollama/ollama.git
synced 2026-09-21 13:38:14 -05:00
model: export capabillity check from model type
This information will be repeated anywhere information from a model is read, so it should be a shared exported function
This commit is contained in:
+3
-20
@@ -189,22 +189,18 @@ func chatTemplateCapabilities(capabilities []model.Capability, chatTemplate stri
|
||||
return capabilities
|
||||
}
|
||||
|
||||
if chatTemplateHasToolSupport(chatTemplate) {
|
||||
if model.ChatTemplateHasToolSupport(chatTemplate) {
|
||||
capabilities = appendCapability(capabilities, model.CapabilityTools)
|
||||
}
|
||||
if chatTemplateHasThinkingSupport(chatTemplate) {
|
||||
if model.ChatTemplateHasThinkingSupport(chatTemplate) {
|
||||
capabilities = appendCapability(capabilities, model.CapabilityThinking)
|
||||
}
|
||||
|
||||
return capabilities
|
||||
}
|
||||
|
||||
func chatTemplateHasToolSupport(chatTemplate string) bool {
|
||||
return strings.Contains(chatTemplate, "tools") || strings.Contains(chatTemplate, "tool_call")
|
||||
}
|
||||
|
||||
func chatTemplateHasToolRoundTrip(chatTemplate string) bool {
|
||||
if !chatTemplateHasToolSupport(chatTemplate) {
|
||||
if !model.ChatTemplateHasToolSupport(chatTemplate) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -220,19 +216,6 @@ func chatTemplateHasToolRoundTrip(chatTemplate string) bool {
|
||||
strings.Contains(chatTemplate, "ipython"))
|
||||
}
|
||||
|
||||
func chatTemplateHasThinkingSupport(chatTemplate string) bool {
|
||||
if strings.Contains(chatTemplate, "<think>") && strings.Contains(chatTemplate, "</think>") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Some Qwen/DeepSeek templates strip prior reasoning by splitting assistant
|
||||
// content at </think>; llama.cpp can still extract reasoning from them.
|
||||
return (strings.Contains(chatTemplate, "content.split('</think>')") ||
|
||||
strings.Contains(chatTemplate, `content.split("</think>")`)) &&
|
||||
!strings.Contains(chatTemplate, "reasoning_content") &&
|
||||
!strings.Contains(chatTemplate, "<SPECIAL_12>")
|
||||
}
|
||||
|
||||
func goTemplateCapabilities(t *template.Template) []model.Capability {
|
||||
if t == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "strings"
|
||||
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
@@ -16,3 +18,24 @@ const (
|
||||
func (c Capability) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// ChatTemplateHasToolSupport reports whether a Jinja chat template references
|
||||
// tools or tool calls.
|
||||
func ChatTemplateHasToolSupport(chatTemplate string) bool {
|
||||
return strings.Contains(chatTemplate, "tools") || strings.Contains(chatTemplate, "tool_call")
|
||||
}
|
||||
|
||||
// ChatTemplateHasThinkingSupport reports whether a Jinja chat template emits
|
||||
// thinking blocks.
|
||||
func ChatTemplateHasThinkingSupport(chatTemplate string) bool {
|
||||
if strings.Contains(chatTemplate, "<think>") && strings.Contains(chatTemplate, "</think>") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Some Qwen/DeepSeek templates strip prior reasoning by splitting assistant
|
||||
// content at </think>; llama.cpp can still extract reasoning from them.
|
||||
return (strings.Contains(chatTemplate, "content.split('</think>')") ||
|
||||
strings.Contains(chatTemplate, `content.split("</think>")`)) &&
|
||||
!strings.Contains(chatTemplate, "reasoning_content") &&
|
||||
!strings.Contains(chatTemplate, "<SPECIAL_12>")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package model
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestChatTemplateCapabilities(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
chatTemplate string
|
||||
wantTools bool
|
||||
wantThinking bool
|
||||
}{
|
||||
{
|
||||
name: "tools variable",
|
||||
chatTemplate: "{% if tools %}{{ tools }}{% endif %}",
|
||||
wantTools: true,
|
||||
},
|
||||
{
|
||||
name: "tool call",
|
||||
chatTemplate: "{% for tool_call in message.tool_calls %}{{ tool_call }}{% endfor %}",
|
||||
wantTools: true,
|
||||
},
|
||||
{
|
||||
name: "thinking tags",
|
||||
chatTemplate: "<think>{{ content }}</think>",
|
||||
wantThinking: true,
|
||||
},
|
||||
{
|
||||
name: "thinking split",
|
||||
chatTemplate: "{% set content = content.split('</think>')[-1] %}",
|
||||
wantThinking: true,
|
||||
},
|
||||
{
|
||||
name: "double quoted thinking split",
|
||||
chatTemplate: `{% set content = content.split("</think>")[-1] %}`,
|
||||
wantThinking: true,
|
||||
},
|
||||
{
|
||||
name: "reasoning content exclusion",
|
||||
chatTemplate: "{% set content = content.split('</think>')[-1] %}{{ reasoning_content }}",
|
||||
},
|
||||
{
|
||||
name: "special token exclusion",
|
||||
chatTemplate: "{% set content = content.split('</think>')[-1] %}<SPECIAL_12>",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ChatTemplateHasToolSupport(tt.chatTemplate); got != tt.wantTools {
|
||||
t.Errorf("ChatTemplateHasToolSupport() = %v, want %v", got, tt.wantTools)
|
||||
}
|
||||
if got := ChatTemplateHasThinkingSupport(tt.chatTemplate); got != tt.wantThinking {
|
||||
t.Errorf("ChatTemplateHasThinkingSupport() = %v, want %v", got, tt.wantThinking)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -528,7 +528,7 @@ func detectCapabilities(modelDir string) modelCapabilities {
|
||||
return modelCapabilities{
|
||||
vision: cfg.VisionConfig != nil || cfg.HasVision,
|
||||
audio: cfg.AudioConfig != nil || cfg.SoundConfig != nil,
|
||||
thinking: chatTemplateHasThinkingSupport(readChatTemplate(modelDir)) ||
|
||||
thinking: model.ChatTemplateHasThinkingSupport(readChatTemplate(modelDir)) ||
|
||||
alwaysSupportsThinking(cfg.Architectures, cfg.ModelType),
|
||||
}
|
||||
}
|
||||
@@ -551,22 +551,6 @@ func readChatTemplate(modelDir string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// chatTemplateHasThinkingSupport reports whether a chat template emits thinking
|
||||
// blocks. Copied from server.chatTemplateHasThinkingSupport so this package need
|
||||
// not depend on the server package for an eight-line string check.
|
||||
func chatTemplateHasThinkingSupport(chatTemplate string) bool {
|
||||
if strings.Contains(chatTemplate, "<think>") && strings.Contains(chatTemplate, "</think>") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Some Qwen/DeepSeek templates strip prior reasoning by splitting assistant
|
||||
// content at </think>; llama.cpp can still extract reasoning from them.
|
||||
return (strings.Contains(chatTemplate, "content.split('</think>')") ||
|
||||
strings.Contains(chatTemplate, `content.split("</think>")`)) &&
|
||||
!strings.Contains(chatTemplate, "reasoning_content") &&
|
||||
!strings.Contains(chatTemplate, "<SPECIAL_12>")
|
||||
}
|
||||
|
||||
func alwaysSupportsThinking(architectures []string, modelType string) bool {
|
||||
if isQwen35Family(modelType) {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user