Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
ollama
Commits
41efdd40
Unverified
Commit
41efdd40
authored
Sep 22, 2025
by
Devon Rifkin
Committed by
GitHub
Sep 22, 2025
Browse files
Merge pull request #12339 from ollama/drifkin/harmony-refactor-to-builtin
harmony: remove special casing in routes.go
parents
c23e6f4c
e7f56ef3
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
144 additions
and
96 deletions
+144
-96
.gitignore
.gitignore
+1
-0
harmony/harmonyparser.go
harmony/harmonyparser.go
+77
-0
model/parsers/parsers.go
model/parsers/parsers.go
+14
-2
model/parsers/qwen3coder.go
model/parsers/qwen3coder.go
+10
-4
server/routes.go
server/routes.go
+42
-90
No files found.
.gitignore
View file @
41efdd40
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
dist
dist
build
build
.cache
.cache
.gocache
*.exe
*.exe
.idea
.idea
test_data
test_data
...
...
harmony/harmonyparser.go
View file @
41efdd40
package
harmony
package
harmony
import
(
import
(
"encoding/json"
"fmt"
"fmt"
"log/slog"
"log/slog"
"strings"
"strings"
...
@@ -265,6 +266,8 @@ type HarmonyMessageHandler struct {
...
@@ -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 {
...
@@ -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 {
...
@@ -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
...
...
model/parsers/parsers.go
View file @
41efdd40
...
@@ -2,10 +2,16 @@ package parsers
...
@@ -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 {
...
@@ -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 {
...
@@ -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
}
}
...
...
model/parsers/qwen3coder.go
View file @
41efdd40
...
@@ -31,6 +31,7 @@ const (
...
@@ -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 {
...
@@ -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
...
@@ -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 {
...
@@ -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 {
...
@@ -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
}
}
...
...
server/routes.go
View file @
41efdd40
...
@@ -34,7 +34,6 @@ import (
...
@@ -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"
...
@@ -291,17 +290,21 @@ func (s *Server) GenerateHandler(c *gin.Context) {
...
@@ -291,17 +290,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
()
if
!
req
.
Raw
&&
m
.
Config
.
Parser
!=
""
{
harmonyToolParser
=
harmonyMessageHandler
.
CreateToolParser
()
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 gptoss models
// Validate Think value: string values currently only allowed for
harmony/
gptoss models
if
req
.
Think
!=
nil
&&
req
.
Think
.
IsString
()
&&
!
useH
armony
{
if
req
.
Think
!=
nil
&&
req
.
Think
.
IsString
()
&&
m
.
Config
.
Parser
!=
"h
armony
"
{
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
}
}
...
@@ -425,7 +428,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
...
@@ -425,7 +428,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
{
...
@@ -462,11 +465,17 @@ func (s *Server) GenerateHandler(c *gin.Context) {
...
@@ -462,11 +465,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
...
@@ -478,26 +487,6 @@ func (s *Server) GenerateHandler(c *gin.Context) {
...
@@ -478,26 +487,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
)
...
@@ -512,7 +501,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
...
@@ -512,7 +501,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
...
@@ -1870,32 +1859,23 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -1870,32 +1859,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
()
var
lastMessage
*
api
.
Message
if
len
(
msgs
)
>
0
{
lastMessage
=
&
msgs
[
len
(
msgs
)
-
1
]
}
harmonyMessageHandler
.
HarmonyParser
.
AddImplicitStartOrPrefill
(
lastMessage
)
harmonyToolParser
=
harmonyMessageHandler
.
CreateToolParser
()
// make a copy of tools to pass to the chat prompt. Function names may be
if
m
.
Config
.
Parser
!=
""
{
// renamed to be valid Harmony function names.
builtinParser
=
parsers
.
ParserForName
(
m
.
Config
.
Parser
)
processedTools
=
make
([]
api
.
Tool
,
len
(
req
.
Tools
))
if
builtinParser
!=
nil
{
copy
(
processedTools
,
req
.
Tools
)
// Determine last message for chat prefill
for
i
,
tool
:=
range
processedTools
{
var
lastMessage
*
api
.
Message
processedTools
[
i
]
.
Function
.
Name
=
harmonyMessageHandler
.
FunctionNameMap
.
ConvertAndAdd
(
tool
.
Function
.
Name
)
if
len
(
msgs
)
>
0
{
lastMessage
=
&
msgs
[
len
(
msgs
)
-
1
]
}
// Initialize parser and get processed tools
processedTools
=
builtinParser
.
Init
(
req
.
Tools
,
lastMessage
)
}
}
}
}
...
@@ -1919,8 +1899,8 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -1919,8 +1899,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
()
&&
!
useH
armony
{
if
req
.
Think
!=
nil
&&
req
.
Think
.
IsString
()
&&
m
.
Config
.
Parser
!=
"h
armony
"
{
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
}
}
...
@@ -1939,7 +1919,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -1939,7 +1919,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
)
}
}
...
@@ -1971,38 +1951,10 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -1971,38 +1951,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
,
r
eq
.
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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment