preserve tool definition and call JSON ordering (#13525)
* preserve tool definition and call JSON ordering This is another iteration of <https://github.com/ollama/ollama/pull/12518>, but this time we've simplified things by relaxing the competing requirements of being compatible AND order-preserving with templates (vs. renderers). We maintain backwards compatibility at the cost of not guaranteeing order for templates. We plan on moving more and more models to renderers, which have been updated to use these new data types, and additionally we could add an opt-in way of templates getting an order-preserved list (e.g., via sibling template vars) * orderedmap_test: remove testify
This commit is contained in:
@@ -272,8 +272,8 @@ func (t *Template) Execute(w io.Writer, v Values) error {
|
||||
} else if !v.forceLegacy && slices.Contains(vars, "messages") {
|
||||
return t.Template.Execute(w, map[string]any{
|
||||
"System": system,
|
||||
"Messages": messages,
|
||||
"Tools": v.Tools,
|
||||
"Messages": convertMessagesForTemplate(messages),
|
||||
"Tools": convertToolsForTemplate(v.Tools),
|
||||
"Response": "",
|
||||
"Think": v.Think,
|
||||
"ThinkLevel": v.ThinkLevel,
|
||||
@@ -373,6 +373,118 @@ func collate(msgs []api.Message) (string, []*api.Message) {
|
||||
return strings.Join(system, "\n\n"), collated
|
||||
}
|
||||
|
||||
// templateTools is a slice of templateTool that marshals to JSON.
|
||||
type templateTools []templateTool
|
||||
|
||||
func (t templateTools) String() string {
|
||||
bts, _ := json.Marshal(t)
|
||||
return string(bts)
|
||||
}
|
||||
|
||||
// templateTool is a template-compatible representation of api.Tool
|
||||
// with Properties as a regular map for template ranging.
|
||||
type templateTool struct {
|
||||
Type string `json:"type"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Function templateToolFunction `json:"function"`
|
||||
}
|
||||
|
||||
type templateToolFunction struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Parameters templateToolFunctionParameters `json:"parameters"`
|
||||
}
|
||||
|
||||
type templateToolFunctionParameters struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Properties map[string]api.ToolProperty `json:"properties"`
|
||||
}
|
||||
|
||||
// templateToolCall is a template-compatible representation of api.ToolCall
|
||||
// with Arguments as a regular map for template ranging.
|
||||
type templateToolCall struct {
|
||||
ID string
|
||||
Function templateToolCallFunction
|
||||
}
|
||||
|
||||
type templateToolCallFunction struct {
|
||||
Index int
|
||||
Name string
|
||||
Arguments map[string]any
|
||||
}
|
||||
|
||||
// templateMessage is a template-compatible representation of api.Message
|
||||
// with ToolCalls converted for template use.
|
||||
type templateMessage struct {
|
||||
Role string
|
||||
Content string
|
||||
Thinking string
|
||||
Images []api.ImageData
|
||||
ToolCalls []templateToolCall
|
||||
ToolName string
|
||||
ToolCallID string
|
||||
}
|
||||
|
||||
// convertToolsForTemplate converts Tools to template-compatible format.
|
||||
func convertToolsForTemplate(tools api.Tools) templateTools {
|
||||
if tools == nil {
|
||||
return nil
|
||||
}
|
||||
result := make(templateTools, len(tools))
|
||||
for i, tool := range tools {
|
||||
result[i] = templateTool{
|
||||
Type: tool.Type,
|
||||
Items: tool.Items,
|
||||
Function: templateToolFunction{
|
||||
Name: tool.Function.Name,
|
||||
Description: tool.Function.Description,
|
||||
Parameters: templateToolFunctionParameters{
|
||||
Type: tool.Function.Parameters.Type,
|
||||
Defs: tool.Function.Parameters.Defs,
|
||||
Items: tool.Function.Parameters.Items,
|
||||
Required: tool.Function.Parameters.Required,
|
||||
Properties: tool.Function.Parameters.Properties.ToMap(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// convertMessagesForTemplate converts Messages to template-compatible format.
|
||||
func convertMessagesForTemplate(messages []*api.Message) []*templateMessage {
|
||||
if messages == nil {
|
||||
return nil
|
||||
}
|
||||
result := make([]*templateMessage, len(messages))
|
||||
for i, msg := range messages {
|
||||
var toolCalls []templateToolCall
|
||||
for _, tc := range msg.ToolCalls {
|
||||
toolCalls = append(toolCalls, templateToolCall{
|
||||
ID: tc.ID,
|
||||
Function: templateToolCallFunction{
|
||||
Index: tc.Function.Index,
|
||||
Name: tc.Function.Name,
|
||||
Arguments: tc.Function.Arguments.ToMap(),
|
||||
},
|
||||
})
|
||||
}
|
||||
result[i] = &templateMessage{
|
||||
Role: msg.Role,
|
||||
Content: msg.Content,
|
||||
Thinking: msg.Thinking,
|
||||
Images: msg.Images,
|
||||
ToolCalls: toolCalls,
|
||||
ToolName: msg.ToolName,
|
||||
ToolCallID: msg.ToolCallID,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Identifiers walks the node tree returning any identifiers it finds along the way
|
||||
func Identifiers(n parse.Node) ([]string, error) {
|
||||
switch n := n.(type) {
|
||||
|
||||
Reference in New Issue
Block a user