mirror of
https://github.com/mudler/LocalAI
synced 2026-04-21 13:27:21 +00:00
Additional thinking tags
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
parent
a352125726
commit
61a6e95f7d
2 changed files with 140 additions and 6 deletions
|
|
@ -4,16 +4,23 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Common thinking/reasoning opening tags used by various models
|
||||
// Common thinking/reasoning opening tags used by various models.
|
||||
// These match the tags detected by llama.cpp in common/chat.cpp
|
||||
var thinkingOpenTags = []string{
|
||||
// DeepSeek R1, V3.1, Nemotron V2, MiniMax M2, Hermes 2 Pro, Granite, Exaone MOE
|
||||
"<think>\n",
|
||||
"<think>",
|
||||
// Generic thinking tags
|
||||
"<thinking>\n",
|
||||
"<thinking>",
|
||||
"<|inner_prefix|>", // Apertus
|
||||
"<|START_THINKING|>", // Command R7B
|
||||
"<seed:think>", // Seed
|
||||
"[THINK]\n", // Magistral
|
||||
// Apertus
|
||||
"<|inner_prefix|>",
|
||||
// Command R7B
|
||||
"<|START_THINKING|>",
|
||||
// Seed
|
||||
"<seed:think>",
|
||||
// Magistral (not in llama.cpp but common)
|
||||
"[THINK]\n",
|
||||
"[THINK]",
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +67,15 @@ func Extract(content string, opts ...Option) (reasoning string, cleanedContent s
|
|||
// All content from the start is treated as reasoning until a closing tag is found.
|
||||
func extractForcedOpen(content string) (reasoning string, cleanedContent string) {
|
||||
// Look for the earliest closing tag
|
||||
closingTags := []string{"</thinking>", "</think>"}
|
||||
// These match the closing tags used by llama.cpp for various models
|
||||
closingTags := []string{
|
||||
"</thinking>",
|
||||
"</think>",
|
||||
"<|END_THINKING|>", // Command R7B
|
||||
"<|inner_suffix|>", // Apertus
|
||||
"</seed:think>", // Seed
|
||||
"[/THINK]", // Magistral
|
||||
}
|
||||
|
||||
earliestCloseIdx := -1
|
||||
var matchedCloseTag string
|
||||
|
|
@ -110,12 +125,17 @@ func extractFromTags(content string) (reasoning string, cleanedContent string) {
|
|||
remaining := content
|
||||
|
||||
// Define tag pairs to look for
|
||||
// These match the tags used by llama.cpp for various models
|
||||
tagPairs := []struct {
|
||||
start string
|
||||
end string
|
||||
}{
|
||||
{"<thinking>", "</thinking>"},
|
||||
{"<think>", "</think>"},
|
||||
{"<|START_THINKING|>", "<|END_THINKING|>"}, // Command R7B
|
||||
{"<|inner_prefix|>", "<|inner_suffix|>"}, // Apertus
|
||||
{"<seed:think>", "</seed:think>"}, // Seed
|
||||
{"[THINK]", "[/THINK]"}, // Magistral
|
||||
}
|
||||
|
||||
// Track the last position we've processed
|
||||
|
|
|
|||
|
|
@ -381,5 +381,119 @@ var _ = Describe("Extract", func() {
|
|||
Expect(reasoning).To(Equal("Reasoning"))
|
||||
Expect(cleaned).To(Equal("content</thinking>more"))
|
||||
})
|
||||
|
||||
It("should handle Command R7B closing tag", func() {
|
||||
content := "Reasoning content<|END_THINKING|>actual response"
|
||||
reasoning, cleaned := Extract(content, WithThinkingForcedOpen())
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle Apertus closing tag", func() {
|
||||
content := "Reasoning content<|inner_suffix|>actual response"
|
||||
reasoning, cleaned := Extract(content, WithThinkingForcedOpen())
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle Seed closing tag", func() {
|
||||
content := "Reasoning content</seed:think>actual response"
|
||||
reasoning, cleaned := Extract(content, WithThinkingForcedOpen())
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle Magistral closing tag", func() {
|
||||
content := "Reasoning content[/THINK]actual response"
|
||||
reasoning, cleaned := Extract(content, WithThinkingForcedOpen())
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with model-specific tag pairs", func() {
|
||||
It("should extract Command R7B reasoning tags", func() {
|
||||
content := "Before <|START_THINKING|>reasoning here<|END_THINKING|> After"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning here"))
|
||||
Expect(cleaned).To(Equal("Before After"))
|
||||
})
|
||||
|
||||
It("should extract Apertus reasoning tags", func() {
|
||||
content := "Before <|inner_prefix|>reasoning here<|inner_suffix|> After"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning here"))
|
||||
Expect(cleaned).To(Equal("Before After"))
|
||||
})
|
||||
|
||||
It("should extract Seed reasoning tags", func() {
|
||||
content := "Before <seed:think>reasoning here</seed:think> After"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning here"))
|
||||
Expect(cleaned).To(Equal("Before After"))
|
||||
})
|
||||
|
||||
It("should extract Magistral reasoning tags", func() {
|
||||
content := "Before [THINK]reasoning here[/THINK] After"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning here"))
|
||||
Expect(cleaned).To(Equal("Before After"))
|
||||
})
|
||||
|
||||
It("should handle unclosed Command R7B tag", func() {
|
||||
content := "Before <|START_THINKING|>reasoning still streaming"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning still streaming"))
|
||||
Expect(cleaned).To(Equal("Before "))
|
||||
})
|
||||
|
||||
It("should handle unclosed Apertus tag", func() {
|
||||
content := "Before <|inner_prefix|>reasoning still streaming"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning still streaming"))
|
||||
Expect(cleaned).To(Equal("Before "))
|
||||
})
|
||||
|
||||
It("should handle unclosed Seed tag", func() {
|
||||
content := "Before <seed:think>reasoning still streaming"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning still streaming"))
|
||||
Expect(cleaned).To(Equal("Before "))
|
||||
})
|
||||
|
||||
It("should handle unclosed Magistral tag", func() {
|
||||
content := "Before [THINK]reasoning still streaming"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("reasoning still streaming"))
|
||||
Expect(cleaned).To(Equal("Before "))
|
||||
})
|
||||
|
||||
It("should handle closing-only Command R7B tag", func() {
|
||||
content := "Reasoning content<|END_THINKING|>actual response"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle closing-only Apertus tag", func() {
|
||||
content := "Reasoning content<|inner_suffix|>actual response"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle closing-only Seed tag", func() {
|
||||
content := "Reasoning content</seed:think>actual response"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
|
||||
It("should handle closing-only Magistral tag", func() {
|
||||
content := "Reasoning content[/THINK]actual response"
|
||||
reasoning, cleaned := Extract(content)
|
||||
Expect(reasoning).To(Equal("Reasoning content"))
|
||||
Expect(cleaned).To(Equal("actual response"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue