mirror of
https://github.com/likelovewant/ollama-for-amd.git
synced 2025-12-23 23:18:26 +00:00
renderers/parsers: olmo3 instruct (#13383)
This commit is contained in:
147
model/renderers/olmo3.go
Normal file
147
model/renderers/olmo3.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package renderers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
)
|
||||
|
||||
const (
|
||||
olmo3DefaultSystemMessage = "You are a helpful function-calling AI assistant. "
|
||||
olmo3NoFunctionsMessage = "You do not currently have access to any functions. "
|
||||
olmo3WithFunctionsMessage = "You are provided with function signatures within <functions></functions> XML tags. You may call one or more functions to assist with the user query. Output any function calls within <function_calls></function_calls> XML tags. Do not make assumptions about what values to plug into functions."
|
||||
)
|
||||
|
||||
type Olmo3Renderer struct{}
|
||||
|
||||
func (r *Olmo3Renderer) Render(messages []api.Message, tools []api.Tool, _ *api.ThinkValue) (string, error) {
|
||||
var sb strings.Builder
|
||||
|
||||
var systemMessage *api.Message
|
||||
filteredMessages := make([]api.Message, 0, len(messages))
|
||||
for i, message := range messages {
|
||||
if message.Role == "system" {
|
||||
if systemMessage == nil {
|
||||
systemMessage = &messages[i]
|
||||
}
|
||||
continue
|
||||
}
|
||||
filteredMessages = append(filteredMessages, message)
|
||||
}
|
||||
|
||||
// Render system message
|
||||
if systemMessage != nil {
|
||||
// Custom system message - single newline after "system"
|
||||
sb.WriteString("<|im_start|>system\n")
|
||||
sb.WriteString(systemMessage.Content)
|
||||
|
||||
if len(tools) > 0 {
|
||||
functionsJSON, err := marshalWithSpaces(tools)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString("<functions>")
|
||||
sb.WriteString(string(functionsJSON))
|
||||
sb.WriteString("</functions>")
|
||||
}
|
||||
sb.WriteString("<|im_end|>\n")
|
||||
} else {
|
||||
// Default system message - single newline after "system"
|
||||
sb.WriteString("<|im_start|>system\n")
|
||||
sb.WriteString(olmo3DefaultSystemMessage)
|
||||
|
||||
if len(tools) > 0 {
|
||||
functionsJSON, err := marshalWithSpaces(tools)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString(olmo3WithFunctionsMessage)
|
||||
sb.WriteString("<functions>")
|
||||
sb.WriteString(string(functionsJSON))
|
||||
sb.WriteString("</functions>")
|
||||
} else {
|
||||
sb.WriteString(olmo3NoFunctionsMessage)
|
||||
sb.WriteString("<functions></functions>")
|
||||
}
|
||||
sb.WriteString("<|im_end|>\n")
|
||||
}
|
||||
|
||||
for i, message := range filteredMessages {
|
||||
lastMessage := i == len(filteredMessages)-1
|
||||
|
||||
switch message.Role {
|
||||
case "user":
|
||||
sb.WriteString("<|im_start|>user\n")
|
||||
sb.WriteString(message.Content)
|
||||
sb.WriteString("<|im_end|>\n")
|
||||
|
||||
case "assistant":
|
||||
sb.WriteString("<|im_start|>assistant\n")
|
||||
|
||||
if message.Content != "" {
|
||||
sb.WriteString(message.Content)
|
||||
}
|
||||
|
||||
if len(message.ToolCalls) > 0 {
|
||||
sb.WriteString("<function_calls>")
|
||||
for j, tc := range message.ToolCalls {
|
||||
// Format as function_name(arg1="value1", arg2="value2")
|
||||
sb.WriteString(tc.Function.Name)
|
||||
sb.WriteString("(")
|
||||
|
||||
// Get sorted keys for deterministic output
|
||||
keys := make([]string, 0, len(tc.Function.Arguments))
|
||||
for k := range tc.Function.Arguments {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for k, key := range keys {
|
||||
if k > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
value, err := json.Marshal(tc.Function.Arguments[key])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%s=%s", key, string(value)))
|
||||
}
|
||||
sb.WriteString(")")
|
||||
|
||||
if j < len(message.ToolCalls)-1 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("</function_calls>")
|
||||
}
|
||||
|
||||
// Add end tag unless it's the last message with content only (prefill)
|
||||
if !lastMessage || len(message.ToolCalls) > 0 {
|
||||
sb.WriteString("<|im_end|>\n")
|
||||
}
|
||||
|
||||
case "tool":
|
||||
sb.WriteString("<|im_start|>environment\n")
|
||||
sb.WriteString(message.Content)
|
||||
sb.WriteString("<|im_end|>\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Add generation prompt if needed
|
||||
needsGenerationPrompt := true
|
||||
if len(filteredMessages) > 0 {
|
||||
lastMsg := filteredMessages[len(filteredMessages)-1]
|
||||
if lastMsg.Role == "assistant" && len(lastMsg.ToolCalls) == 0 && lastMsg.Content != "" {
|
||||
needsGenerationPrompt = false
|
||||
}
|
||||
}
|
||||
|
||||
if needsGenerationPrompt {
|
||||
sb.WriteString("<|im_start|>assistant\n\n")
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user