mirror of
https://github.com/ollama/ollama.git
synced 2025-08-26 22:41:47 +02:00
harmony: move harmony parsing into a package (#12016)
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
package server
|
package harmony
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
@@ -20,18 +19,6 @@ const (
|
|||||||
harmonyParserState_ParsingContent
|
harmonyParserState_ParsingContent
|
||||||
)
|
)
|
||||||
|
|
||||||
func shouldUseHarmony(model Model) bool {
|
|
||||||
if slices.Contains([]string{"gptoss", "gpt-oss"}, model.Config.ModelFamily) {
|
|
||||||
// heuristic to check whether the template expects to be parsed via harmony:
|
|
||||||
// search for harmony tags that are nearly always used
|
|
||||||
if model.Template.Contains("<|start|>") && model.Template.Contains("<|end|>") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s harmonyParserState) String() string {
|
func (s harmonyParserState) String() string {
|
||||||
switch s {
|
switch s {
|
||||||
// we're looking for the message start tag
|
// we're looking for the message start tag
|
||||||
@@ -277,20 +264,20 @@ const (
|
|||||||
// This is a higher level interface that maps harmony concepts into ollama concepts
|
// This is a higher level interface that maps harmony concepts into ollama concepts
|
||||||
type HarmonyMessageHandler struct {
|
type HarmonyMessageHandler struct {
|
||||||
state harmonyMessageState
|
state harmonyMessageState
|
||||||
harmonyParser *HarmonyParser
|
HarmonyParser *HarmonyParser
|
||||||
functionNameMap *FunctionNameMap
|
FunctionNameMap *FunctionNameMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHarmonyMessageHandler creates a new message handler
|
// NewHarmonyMessageHandler creates a new message handler
|
||||||
func NewHarmonyMessageHandler() *HarmonyMessageHandler {
|
func NewHarmonyMessageHandler() *HarmonyMessageHandler {
|
||||||
return &HarmonyMessageHandler{
|
return &HarmonyMessageHandler{
|
||||||
state: harmonyMessageState_Normal,
|
state: harmonyMessageState_Normal,
|
||||||
harmonyParser: &HarmonyParser{
|
HarmonyParser: &HarmonyParser{
|
||||||
MessageStartTag: "<|start|>",
|
MessageStartTag: "<|start|>",
|
||||||
MessageEndTag: "<|end|>",
|
MessageEndTag: "<|end|>",
|
||||||
HeaderEndTag: "<|message|>",
|
HeaderEndTag: "<|message|>",
|
||||||
},
|
},
|
||||||
functionNameMap: NewFunctionNameMap(),
|
FunctionNameMap: NewFunctionNameMap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +288,7 @@ func (h *HarmonyMessageHandler) AddContent(content string, toolParser *HarmonyTo
|
|||||||
thinkingSb := strings.Builder{}
|
thinkingSb := strings.Builder{}
|
||||||
toolContentSb := strings.Builder{}
|
toolContentSb := strings.Builder{}
|
||||||
|
|
||||||
events := h.harmonyParser.AddContent(content)
|
events := h.HarmonyParser.AddContent(content)
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch event := event.(type) {
|
switch event := event.(type) {
|
||||||
case HarmonyEventHeaderComplete:
|
case HarmonyEventHeaderComplete:
|
@@ -1,4 +1,4 @@
|
|||||||
package server
|
package harmony
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/ollama/ollama/envconfig"
|
"github.com/ollama/ollama/envconfig"
|
||||||
"github.com/ollama/ollama/format"
|
"github.com/ollama/ollama/format"
|
||||||
"github.com/ollama/ollama/fs/ggml"
|
"github.com/ollama/ollama/fs/ggml"
|
||||||
|
"github.com/ollama/ollama/harmony"
|
||||||
"github.com/ollama/ollama/llm"
|
"github.com/ollama/ollama/llm"
|
||||||
"github.com/ollama/ollama/logutil"
|
"github.com/ollama/ollama/logutil"
|
||||||
"github.com/ollama/ollama/openai"
|
"github.com/ollama/ollama/openai"
|
||||||
@@ -45,6 +46,18 @@ import (
|
|||||||
"github.com/ollama/ollama/version"
|
"github.com/ollama/ollama/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func shouldUseHarmony(model *Model) bool {
|
||||||
|
if slices.Contains([]string{"gptoss", "gpt-oss"}, model.Config.ModelFamily) {
|
||||||
|
// heuristic to check whether the template expects to be parsed via harmony:
|
||||||
|
// search for harmony tags that are nearly always used
|
||||||
|
if model.Template.Contains("<|start|>") && model.Template.Contains("<|end|>") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func experimentEnabled(name string) bool {
|
func experimentEnabled(name string) bool {
|
||||||
return slices.Contains(strings.Split(os.Getenv("OLLAMA_EXPERIMENT"), ","), name)
|
return slices.Contains(strings.Split(os.Getenv("OLLAMA_EXPERIMENT"), ","), name)
|
||||||
}
|
}
|
||||||
@@ -194,12 +207,12 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
useHarmony := shouldUseHarmony(*m) && !req.Raw
|
useHarmony := shouldUseHarmony(m) && !req.Raw
|
||||||
var harmonyMessageHandler *HarmonyMessageHandler
|
var harmonyMessageHandler *harmony.HarmonyMessageHandler
|
||||||
var harmonyToolParser *HarmonyToolCallAccumulator
|
var harmonyToolParser *harmony.HarmonyToolCallAccumulator
|
||||||
if useHarmony {
|
if useHarmony {
|
||||||
harmonyMessageHandler = NewHarmonyMessageHandler()
|
harmonyMessageHandler = harmony.NewHarmonyMessageHandler()
|
||||||
harmonyMessageHandler.harmonyParser.AddImplicitStart()
|
harmonyMessageHandler.HarmonyParser.AddImplicitStart()
|
||||||
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1603,19 +1616,19 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
msgs = filterThinkTags(msgs, m)
|
msgs = filterThinkTags(msgs, m)
|
||||||
|
|
||||||
var harmonyMessageHandler *HarmonyMessageHandler
|
var harmonyMessageHandler *harmony.HarmonyMessageHandler
|
||||||
var harmonyToolParser *HarmonyToolCallAccumulator
|
var harmonyToolParser *harmony.HarmonyToolCallAccumulator
|
||||||
|
|
||||||
useHarmony := shouldUseHarmony(*m)
|
useHarmony := shouldUseHarmony(m)
|
||||||
|
|
||||||
processedTools := req.Tools
|
processedTools := req.Tools
|
||||||
if useHarmony {
|
if useHarmony {
|
||||||
harmonyMessageHandler = NewHarmonyMessageHandler()
|
harmonyMessageHandler = harmony.NewHarmonyMessageHandler()
|
||||||
var lastMessage *api.Message
|
var lastMessage *api.Message
|
||||||
if len(msgs) > 0 {
|
if len(msgs) > 0 {
|
||||||
lastMessage = &msgs[len(msgs)-1]
|
lastMessage = &msgs[len(msgs)-1]
|
||||||
}
|
}
|
||||||
harmonyMessageHandler.harmonyParser.AddImplicitStartOrPrefill(lastMessage)
|
harmonyMessageHandler.HarmonyParser.AddImplicitStartOrPrefill(lastMessage)
|
||||||
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
||||||
|
|
||||||
// make a copy of tools to pass to the chat prompt. Function names may be
|
// make a copy of tools to pass to the chat prompt. Function names may be
|
||||||
@@ -1623,7 +1636,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
|||||||
processedTools = make([]api.Tool, len(req.Tools))
|
processedTools = make([]api.Tool, len(req.Tools))
|
||||||
copy(processedTools, req.Tools)
|
copy(processedTools, req.Tools)
|
||||||
for i, tool := range processedTools {
|
for i, tool := range processedTools {
|
||||||
processedTools[i].Function.Name = harmonyMessageHandler.functionNameMap.ConvertAndAdd(tool.Function.Name)
|
processedTools[i].Function.Name = harmonyMessageHandler.FunctionNameMap.ConvertAndAdd(tool.Function.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1705,7 +1718,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
|||||||
toolName, toolContent := harmonyToolParser.Drain()
|
toolName, toolContent := harmonyToolParser.Drain()
|
||||||
if toolName != nil {
|
if toolName != nil {
|
||||||
*toolName = strings.TrimPrefix(*toolName, "functions.")
|
*toolName = strings.TrimPrefix(*toolName, "functions.")
|
||||||
*toolName = harmonyMessageHandler.functionNameMap.OriginalFromConverted(*toolName)
|
*toolName = harmonyMessageHandler.FunctionNameMap.OriginalFromConverted(*toolName)
|
||||||
var args api.ToolCallFunctionArguments
|
var args api.ToolCallFunctionArguments
|
||||||
if err := json.Unmarshal([]byte(toolContent), &args); err != nil {
|
if err := json.Unmarshal([]byte(toolContent), &args); err != nil {
|
||||||
errStr := fmt.Sprintf("error parsing tool call: raw='%s', err=%s", toolContent, err.Error())
|
errStr := fmt.Sprintf("error parsing tool call: raw='%s', err=%s", toolContent, err.Error())
|
||||||
|
Reference in New Issue
Block a user