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
7cce5aac
Unverified
Commit
7cce5aac
authored
Aug 21, 2025
by
Parth Sareen
Committed by
GitHub
Aug 21, 2025
Browse files
harmony: move harmony parsing into a package (#12016)
parent
4ae4f47b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
32 additions
and
32 deletions
+32
-32
harmony/harmonyparser.go
harmony/harmonyparser.go
+6
-19
harmony/harmonyparser_test.go
harmony/harmonyparser_test.go
+1
-1
server/routes.go
server/routes.go
+25
-12
No files found.
server
/harmonyparser.go
→
harmony
/harmonyparser.go
View file @
7cce5aac
package
server
package
harmony
import
(
import
(
"context"
"context"
"fmt"
"fmt"
"log/slog"
"log/slog"
"slices"
"strings"
"strings"
"unicode"
"unicode"
...
@@ -20,18 +19,6 @@ const (
...
@@ -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 (
...
@@ -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
h
armonyParser
*
HarmonyParser
H
armonyParser
*
HarmonyParser
f
unctionNameMap
*
FunctionNameMap
F
unctionNameMap
*
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
,
h
armonyParser
:
&
HarmonyParser
{
H
armonyParser
:
&
HarmonyParser
{
MessageStartTag
:
"<|start|>"
,
MessageStartTag
:
"<|start|>"
,
MessageEndTag
:
"<|end|>"
,
MessageEndTag
:
"<|end|>"
,
HeaderEndTag
:
"<|message|>"
,
HeaderEndTag
:
"<|message|>"
,
},
},
f
unctionNameMap
:
NewFunctionNameMap
(),
F
unctionNameMap
:
NewFunctionNameMap
(),
}
}
}
}
...
@@ -301,7 +288,7 @@ func (h *HarmonyMessageHandler) AddContent(content string, toolParser *HarmonyTo
...
@@ -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
.
h
armonyParser
.
AddContent
(
content
)
events
:=
h
.
H
armonyParser
.
AddContent
(
content
)
for
_
,
event
:=
range
events
{
for
_
,
event
:=
range
events
{
switch
event
:=
event
.
(
type
)
{
switch
event
:=
event
.
(
type
)
{
case
HarmonyEventHeaderComplete
:
case
HarmonyEventHeaderComplete
:
...
...
server
/harmonyparser_test.go
→
harmony
/harmonyparser_test.go
View file @
7cce5aac
package
server
package
harmony
import
(
import
(
"fmt"
"fmt"
...
...
server/routes.go
View file @
7cce5aac
...
@@ -32,6 +32,7 @@ import (
...
@@ -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 (
...
@@ -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) {
...
@@ -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
.
h
armonyParser
.
AddImplicitStart
()
harmonyMessageHandler
.
H
armonyParser
.
AddImplicitStart
()
harmonyToolParser
=
harmonyMessageHandler
.
CreateToolParser
()
harmonyToolParser
=
harmonyMessageHandler
.
CreateToolParser
()
}
}
...
@@ -1603,19 +1616,19 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -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
.
h
armonyParser
.
AddImplicitStartOrPrefill
(
lastMessage
)
harmonyMessageHandler
.
H
armonyParser
.
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) {
...
@@ -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
.
f
unctionNameMap
.
ConvertAndAdd
(
tool
.
Function
.
Name
)
processedTools
[
i
]
.
Function
.
Name
=
harmonyMessageHandler
.
F
unctionNameMap
.
ConvertAndAdd
(
tool
.
Function
.
Name
)
}
}
}
}
...
@@ -1705,7 +1718,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
...
@@ -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
.
f
unctionNameMap
.
OriginalFromConverted
(
*
toolName
)
*
toolName
=
harmonyMessageHandler
.
F
unctionNameMap
.
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
())
...
...
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