mirror of https://github.com/ollama/ollama.git
harmony: remove special casing in routes.go
Now that we have a built-in parser abstraction, which was introduced in <https://github.com/ollama/ollama/pull/12248>, we can modify our harmony parser to match this and then get rid of nearly all of the harmony-specific logic in routes.go. We do have a small amount of code that turns the parser on by default if the architecture matches and no other built-in parser was provided. The built-in parser interface was modified in order to handle harmony's prefill and tool name translation requirements.
This commit is contained in:
parent
eb0a5d4459
commit
e7f56ef3d8
|
@ -6,6 +6,7 @@
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
.cache
|
.cache
|
||||||
|
.gocache
|
||||||
*.exe
|
*.exe
|
||||||
.idea
|
.idea
|
||||||
test_data
|
test_data
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package harmony
|
package harmony
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -265,6 +266,8 @@ type HarmonyMessageHandler struct {
|
||||||
state harmonyMessageState
|
state harmonyMessageState
|
||||||
HarmonyParser *HarmonyParser
|
HarmonyParser *HarmonyParser
|
||||||
FunctionNameMap *FunctionNameMap
|
FunctionNameMap *FunctionNameMap
|
||||||
|
toolAccumulator *HarmonyToolCallAccumulator
|
||||||
|
convertedTools map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHarmonyMessageHandler creates a new message handler
|
// NewHarmonyMessageHandler creates a new message handler
|
||||||
|
@ -277,6 +280,7 @@ func NewHarmonyMessageHandler() *HarmonyMessageHandler {
|
||||||
HeaderEndTag: "<|message|>",
|
HeaderEndTag: "<|message|>",
|
||||||
},
|
},
|
||||||
FunctionNameMap: NewFunctionNameMap(),
|
FunctionNameMap: NewFunctionNameMap(),
|
||||||
|
convertedTools: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,6 +388,79 @@ func NewFunctionNameMap() *FunctionNameMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init initializes the handler with tools and optional last message
|
||||||
|
// Implements the Parser interface
|
||||||
|
func (h *HarmonyMessageHandler) Init(tools []api.Tool, lastMessage *api.Message) []api.Tool {
|
||||||
|
// Initialize the harmony parser
|
||||||
|
if h.HarmonyParser == nil {
|
||||||
|
h.HarmonyParser = &HarmonyParser{
|
||||||
|
MessageStartTag: "<|start|>",
|
||||||
|
MessageEndTag: "<|end|>",
|
||||||
|
HeaderEndTag: "<|message|>",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle prefill for chat mode
|
||||||
|
if lastMessage != nil {
|
||||||
|
h.HarmonyParser.AddImplicitStartOrPrefill(lastMessage)
|
||||||
|
} else {
|
||||||
|
h.HarmonyParser.AddImplicitStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize tool accumulator
|
||||||
|
h.toolAccumulator = h.CreateToolParser()
|
||||||
|
|
||||||
|
// Process tools and return renamed versions
|
||||||
|
if len(tools) == 0 {
|
||||||
|
return tools
|
||||||
|
}
|
||||||
|
|
||||||
|
processedTools := make([]api.Tool, len(tools))
|
||||||
|
copy(processedTools, tools)
|
||||||
|
for i, tool := range processedTools {
|
||||||
|
if tool.Function.Name != "" {
|
||||||
|
processedTools[i].Function.Name = h.FunctionNameMap.ConvertAndAdd(tool.Function.Name)
|
||||||
|
h.convertedTools[tool.Function.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processedTools
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add implements the Parser interface - processes streamed content and extracts content, thinking, and tool calls
|
||||||
|
func (h *HarmonyMessageHandler) Add(s string, done bool) (content string, thinking string, calls []api.ToolCall, err error) {
|
||||||
|
content, thinking, toolContent := h.AddContent(s, h.toolAccumulator)
|
||||||
|
if toolContent != "" {
|
||||||
|
h.toolAccumulator.Add(toolContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tool calls always happen one at a time, and always at the end of a message,
|
||||||
|
// so for simplicity we defer parsing them until we know we're done
|
||||||
|
if done {
|
||||||
|
toolName, raw := h.toolAccumulator.Drain()
|
||||||
|
if toolName != nil {
|
||||||
|
name := strings.TrimPrefix(*toolName, "functions.")
|
||||||
|
name = h.FunctionNameMap.OriginalFromConverted(name)
|
||||||
|
var args api.ToolCallFunctionArguments
|
||||||
|
if err := json.Unmarshal([]byte(raw), &args); err != nil {
|
||||||
|
return "", "", nil, fmt.Errorf("error parsing tool call: raw='%s', err=%w", raw, err)
|
||||||
|
}
|
||||||
|
calls = append(calls, api.ToolCall{Function: api.ToolCallFunction{Name: name, Arguments: args}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, thinking, calls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasToolSupport implements the Parser interface
|
||||||
|
func (h *HarmonyMessageHandler) HasToolSupport() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasThinkingSupport implements the Parser interface
|
||||||
|
func (h *HarmonyMessageHandler) HasThinkingSupport() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (m *FunctionNameMap) ConvertAndAdd(userFunctionName string) string {
|
func (m *FunctionNameMap) ConvertAndAdd(userFunctionName string) string {
|
||||||
harmonyFunctionName := m.deriveName(userFunctionName)
|
harmonyFunctionName := m.deriveName(userFunctionName)
|
||||||
m.userToHarmony[userFunctionName] = harmonyFunctionName
|
m.userToHarmony[userFunctionName] = harmonyFunctionName
|
||||||
|
|
|
@ -2,10 +2,16 @@ package parsers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ollama/ollama/api"
|
"github.com/ollama/ollama/api"
|
||||||
|
"github.com/ollama/ollama/harmony"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Parser interface {
|
type Parser interface {
|
||||||
Add(s string, tools []api.Tool) (content string, thinking string, calls []api.ToolCall, err error)
|
// Init initializes the parser with tools and optional last message for chat prefill
|
||||||
|
// Returns processed tools if the parser needs to modify them (e.g., harmony renames them)
|
||||||
|
Init(tools []api.Tool, lastMessage *api.Message) []api.Tool
|
||||||
|
// Add processes streamed content and returns parsed content, thinking, and tool calls
|
||||||
|
// The done flag indicates if this is the last chunk (used for draining accumulators)
|
||||||
|
Add(s string, done bool) (content string, thinking string, calls []api.ToolCall, err error)
|
||||||
HasToolSupport() bool
|
HasToolSupport() bool
|
||||||
HasThinkingSupport() bool
|
HasThinkingSupport() bool
|
||||||
}
|
}
|
||||||
|
@ -17,6 +23,8 @@ func ParserForName(name string) Parser {
|
||||||
return parser
|
return parser
|
||||||
case "passthrough":
|
case "passthrough":
|
||||||
return &PassthroughParser{}
|
return &PassthroughParser{}
|
||||||
|
case "harmony":
|
||||||
|
return harmony.NewHarmonyMessageHandler()
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -24,7 +32,11 @@ func ParserForName(name string) Parser {
|
||||||
|
|
||||||
type PassthroughParser struct{}
|
type PassthroughParser struct{}
|
||||||
|
|
||||||
func (p *PassthroughParser) Add(s string, tools []api.Tool) (content string, thinking string, calls []api.ToolCall, err error) {
|
func (p *PassthroughParser) Init(tools []api.Tool, lastMessage *api.Message) []api.Tool {
|
||||||
|
return tools // passthrough doesn't modify tools
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PassthroughParser) Add(s string, done bool) (content string, thinking string, calls []api.ToolCall, err error) {
|
||||||
return s, "", nil, nil
|
return s, "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ const (
|
||||||
type Qwen3CoderParser struct {
|
type Qwen3CoderParser struct {
|
||||||
state qwenParserState
|
state qwenParserState
|
||||||
acc strings.Builder
|
acc strings.Builder
|
||||||
|
tools []api.Tool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Qwen3CoderParser) HasToolSupport() bool {
|
func (p *Qwen3CoderParser) HasToolSupport() bool {
|
||||||
|
@ -41,7 +42,12 @@ func (p *Qwen3CoderParser) HasThinkingSupport() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Qwen3CoderParser) Add(s string, tools []api.Tool) (content string, thinking string, calls []api.ToolCall, err error) {
|
func (p *Qwen3CoderParser) Init(tools []api.Tool, lastMessage *api.Message) []api.Tool {
|
||||||
|
p.tools = tools
|
||||||
|
return tools // Qwen doesn't modify tools
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Qwen3CoderParser) Add(s string, done bool) (content string, thinking string, calls []api.ToolCall, err error) {
|
||||||
p.acc.WriteString(s)
|
p.acc.WriteString(s)
|
||||||
|
|
||||||
events := p.parseEvents()
|
events := p.parseEvents()
|
||||||
|
@ -51,7 +57,7 @@ func (p *Qwen3CoderParser) Add(s string, tools []api.Tool) (content string, thin
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
switch event := event.(type) {
|
switch event := event.(type) {
|
||||||
case qwenEventRawToolCall:
|
case qwenEventRawToolCall:
|
||||||
toolCall, err := parseToolCall(event, tools)
|
toolCall, err := parseToolCall(event, p.tools)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn("qwen tool call parsing failed", "error", err)
|
slog.Warn("qwen tool call parsing failed", "error", err)
|
||||||
return "", "", nil, err
|
return "", "", nil, err
|
||||||
|
@ -359,7 +365,7 @@ func parseValue(raw string, paramType api.PropertyType) any {
|
||||||
|
|
||||||
// Try array
|
// Try array
|
||||||
if typeSet["array"] {
|
if typeSet["array"] {
|
||||||
var arr []interface{}
|
var arr []any
|
||||||
if err := json.Unmarshal([]byte(raw), &arr); err == nil {
|
if err := json.Unmarshal([]byte(raw), &arr); err == nil {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
@ -371,7 +377,7 @@ func parseValue(raw string, paramType api.PropertyType) any {
|
||||||
|
|
||||||
// Try object
|
// Try object
|
||||||
if typeSet["object"] {
|
if typeSet["object"] {
|
||||||
var obj map[string]interface{}
|
var obj map[string]any
|
||||||
if err := json.Unmarshal([]byte(raw), &obj); err == nil {
|
if err := json.Unmarshal([]byte(raw), &obj); err == nil {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
126
server/routes.go
126
server/routes.go
|
@ -34,7 +34,6 @@ 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/model/parsers"
|
"github.com/ollama/ollama/model/parsers"
|
||||||
|
@ -288,17 +287,21 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
useHarmony := shouldUseHarmony(m) && !req.Raw
|
var builtinParser parsers.Parser
|
||||||
var harmonyMessageHandler *harmony.HarmonyMessageHandler
|
if shouldUseHarmony(m) && m.Config.Parser == "" {
|
||||||
var harmonyToolParser *harmony.HarmonyToolCallAccumulator
|
m.Config.Parser = "harmony"
|
||||||
if useHarmony {
|
|
||||||
harmonyMessageHandler = harmony.NewHarmonyMessageHandler()
|
|
||||||
harmonyMessageHandler.HarmonyParser.AddImplicitStart()
|
|
||||||
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Think value: string values currently only allowed for gptoss models
|
if !req.Raw && m.Config.Parser != "" {
|
||||||
if req.Think != nil && req.Think.IsString() && !useHarmony {
|
builtinParser = parsers.ParserForName(m.Config.Parser)
|
||||||
|
if builtinParser != nil {
|
||||||
|
// no tools or last message for generate endpoint
|
||||||
|
builtinParser.Init(nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate Think value: string values currently only allowed for harmony/gptoss models
|
||||||
|
if req.Think != nil && req.Think.IsString() && m.Config.Parser != "harmony" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("think value %q is not supported for this model", req.Think.String())})
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("think value %q is not supported for this model", req.Think.String())})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -422,7 +425,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var thinkingState *thinking.Parser
|
var thinkingState *thinking.Parser
|
||||||
if !useHarmony {
|
if builtinParser == nil {
|
||||||
openingTag, closingTag := thinking.InferTags(m.Template.Template)
|
openingTag, closingTag := thinking.InferTags(m.Template.Template)
|
||||||
if req.Think != nil && req.Think.Bool() && openingTag != "" && closingTag != "" {
|
if req.Think != nil && req.Think.Bool() && openingTag != "" && closingTag != "" {
|
||||||
thinkingState = &thinking.Parser{
|
thinkingState = &thinking.Parser{
|
||||||
|
@ -459,11 +462,17 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if useHarmony {
|
if builtinParser != nil {
|
||||||
content, thinking, toolContent := harmonyMessageHandler.AddContent(cr.Content, harmonyToolParser)
|
content, thinking, toolCalls, err := builtinParser.Add(cr.Content, cr.Done)
|
||||||
|
if err != nil {
|
||||||
|
ch <- gin.H{"error": err.Error()}
|
||||||
|
return
|
||||||
|
}
|
||||||
res.Response = content
|
res.Response = content
|
||||||
res.Thinking = thinking
|
res.Thinking = thinking
|
||||||
harmonyToolParser.Add(toolContent)
|
if cr.Done && len(toolCalls) > 0 {
|
||||||
|
res.ToolCalls = toolCalls
|
||||||
|
}
|
||||||
} else if thinkingState != nil {
|
} else if thinkingState != nil {
|
||||||
thinking, content := thinkingState.AddContent(cr.Content)
|
thinking, content := thinkingState.AddContent(cr.Content)
|
||||||
res.Thinking = thinking
|
res.Thinking = thinking
|
||||||
|
@ -475,26 +484,6 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cr.Done {
|
if cr.Done {
|
||||||
if useHarmony {
|
|
||||||
toolName, toolContent := harmonyToolParser.Drain()
|
|
||||||
if toolName != nil {
|
|
||||||
*toolName = strings.TrimPrefix(*toolName, "functions.")
|
|
||||||
var args api.ToolCallFunctionArguments
|
|
||||||
if err := json.Unmarshal([]byte(toolContent), &args); err != nil {
|
|
||||||
errStr := fmt.Sprintf("error parsing tool call: raw='%s', err=%s", toolContent, err.Error())
|
|
||||||
ch <- gin.H{"error": errStr}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res.ToolCalls = append(res.ToolCalls, api.ToolCall{
|
|
||||||
Function: api.ToolCallFunction{
|
|
||||||
Name: *toolName,
|
|
||||||
Arguments: args,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.DoneReason = cr.DoneReason.String()
|
res.DoneReason = cr.DoneReason.String()
|
||||||
res.TotalDuration = time.Since(checkpointStart)
|
res.TotalDuration = time.Since(checkpointStart)
|
||||||
res.LoadDuration = checkpointLoaded.Sub(checkpointStart)
|
res.LoadDuration = checkpointLoaded.Sub(checkpointStart)
|
||||||
|
@ -509,7 +498,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if useHarmony {
|
if builtinParser != nil {
|
||||||
// only send messages with meaningful content (empty messages confuse clients)
|
// only send messages with meaningful content (empty messages confuse clients)
|
||||||
if res.Response != "" || res.Thinking != "" || res.Done || len(res.ToolCalls) > 0 {
|
if res.Response != "" || res.Thinking != "" || res.Done || len(res.ToolCalls) > 0 {
|
||||||
ch <- res
|
ch <- res
|
||||||
|
@ -1853,32 +1842,23 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
msgs = filterThinkTags(msgs, m)
|
msgs = filterThinkTags(msgs, m)
|
||||||
|
|
||||||
var builtinParser parsers.Parser
|
if shouldUseHarmony(m) && m.Config.Parser == "" {
|
||||||
if m.Config.Parser != "" {
|
m.Config.Parser = "harmony"
|
||||||
builtinParser = parsers.ParserForName(m.Config.Parser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var harmonyMessageHandler *harmony.HarmonyMessageHandler
|
var builtinParser parsers.Parser
|
||||||
var harmonyToolParser *harmony.HarmonyToolCallAccumulator
|
|
||||||
|
|
||||||
useHarmony := shouldUseHarmony(m) || m.Config.Parser == "harmony"
|
|
||||||
|
|
||||||
processedTools := req.Tools
|
processedTools := req.Tools
|
||||||
if useHarmony {
|
|
||||||
harmonyMessageHandler = harmony.NewHarmonyMessageHandler()
|
if m.Config.Parser != "" {
|
||||||
|
builtinParser = parsers.ParserForName(m.Config.Parser)
|
||||||
|
if builtinParser != nil {
|
||||||
|
// Determine last message for chat prefill
|
||||||
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)
|
// Initialize parser and get processed tools
|
||||||
harmonyToolParser = harmonyMessageHandler.CreateToolParser()
|
processedTools = builtinParser.Init(req.Tools, lastMessage)
|
||||||
|
|
||||||
// make a copy of tools to pass to the chat prompt. Function names may be
|
|
||||||
// renamed to be valid Harmony function names.
|
|
||||||
processedTools = make([]api.Tool, len(req.Tools))
|
|
||||||
copy(processedTools, req.Tools)
|
|
||||||
for i, tool := range processedTools {
|
|
||||||
processedTools[i].Function.Name = harmonyMessageHandler.FunctionNameMap.ConvertAndAdd(tool.Function.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1902,8 +1882,8 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Think value: string values currently only allowed for gptoss models
|
// Validate Think value: string values currently only allowed for harmony/gptoss models
|
||||||
if req.Think != nil && req.Think.IsString() && !useHarmony {
|
if req.Think != nil && req.Think.IsString() && m.Config.Parser != "harmony" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("think value %q is not supported for this model", req.Think.String())})
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("think value %q is not supported for this model", req.Think.String())})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1922,7 +1902,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolParser *tools.Parser
|
var toolParser *tools.Parser
|
||||||
if len(req.Tools) > 0 && !useHarmony {
|
if len(req.Tools) > 0 && (builtinParser == nil || !builtinParser.HasToolSupport()) {
|
||||||
toolParser = tools.NewParser(m.Template.Template, req.Tools)
|
toolParser = tools.NewParser(m.Template.Template, req.Tools)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1954,38 +1934,10 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
||||||
res.LoadDuration = checkpointLoaded.Sub(checkpointStart)
|
res.LoadDuration = checkpointLoaded.Sub(checkpointStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(drifkin): fold this as much as possibleinto the generic m.Config.Parser logic
|
if builtinParser != nil {
|
||||||
if useHarmony {
|
|
||||||
content, thinking, toolContent := harmonyMessageHandler.AddContent(r.Content, harmonyToolParser)
|
|
||||||
res.Message.Content = content
|
|
||||||
res.Message.Thinking = thinking
|
|
||||||
harmonyToolParser.Add(toolContent)
|
|
||||||
|
|
||||||
if r.Done {
|
|
||||||
toolName, toolContent := harmonyToolParser.Drain()
|
|
||||||
if toolName != nil {
|
|
||||||
*toolName = strings.TrimPrefix(*toolName, "functions.")
|
|
||||||
*toolName = harmonyMessageHandler.FunctionNameMap.OriginalFromConverted(*toolName)
|
|
||||||
var args api.ToolCallFunctionArguments
|
|
||||||
if err := json.Unmarshal([]byte(toolContent), &args); err != nil {
|
|
||||||
errStr := fmt.Sprintf("error parsing tool call: raw='%s', err=%s", toolContent, err.Error())
|
|
||||||
ch <- gin.H{"error": errStr}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.Message.ToolCalls = []api.ToolCall{{Function: api.ToolCallFunction{Name: *toolName, Arguments: args}}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only send messages with meaningful content (empty messages confuse clients)
|
|
||||||
if res.Message.Content != "" || res.Message.Thinking != "" || len(res.Message.ToolCalls) > 0 || res.Done {
|
|
||||||
ch <- res
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
} else if builtinParser != nil {
|
|
||||||
slog.Log(context.TODO(), logutil.LevelTrace, "builtin parser input", "parser", m.Config.Parser, "content", r.Content)
|
slog.Log(context.TODO(), logutil.LevelTrace, "builtin parser input", "parser", m.Config.Parser, "content", r.Content)
|
||||||
|
|
||||||
content, thinking, toolCalls, err := builtinParser.Add(r.Content, req.Tools)
|
content, thinking, toolCalls, err := builtinParser.Add(r.Content, r.Done)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch <- gin.H{"error": err.Error()}
|
ch <- gin.H{"error": err.Error()}
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue