mirror of
https://github.com/ollama/ollama.git
synced 2026-04-17 23:54:05 +02:00
This change adds a new MLX based runner which includes: * Method-based MLX bindings * Subprocess-based MLX runner (x/mlxrunner) * KV cache with tree management * A basic sampler The GLM4-MoE-Lite model has been ported to use the new bindings. --------- Co-authored-by: Michael Yang <git@mxy.ng>
193 lines
4.7 KiB
Go
193 lines
4.7 KiB
Go
//go:build mlx
|
|
|
|
package glm4_moe_lite
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/ollama/ollama/api"
|
|
)
|
|
|
|
func TestParserThinking(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
thinkEnabled bool
|
|
wantContent string
|
|
wantThinking string
|
|
wantToolCalls int
|
|
}{
|
|
{
|
|
name: "thinking enabled - simple thinking then content",
|
|
input: "Let me think about this...</think>Here is my answer.",
|
|
thinkEnabled: true,
|
|
wantThinking: "Let me think about this...",
|
|
wantContent: "Here is my answer.",
|
|
},
|
|
{
|
|
name: "thinking enabled - only thinking",
|
|
input: "I need to consider multiple factors...",
|
|
thinkEnabled: true,
|
|
wantThinking: "I need to consider multiple factors...",
|
|
wantContent: "",
|
|
},
|
|
{
|
|
name: "thinking disabled - direct content",
|
|
input: "Here is my direct answer.",
|
|
thinkEnabled: false,
|
|
wantThinking: "",
|
|
wantContent: "Here is my direct answer.",
|
|
},
|
|
{
|
|
name: "thinking with tool call",
|
|
input: "Let me search for that...</think>I'll use a tool.<tool_call>search<arg_key>query</arg_key><arg_value>test</arg_value></tool_call>",
|
|
thinkEnabled: true,
|
|
wantThinking: "Let me search for that...",
|
|
wantContent: "I'll use a tool.",
|
|
wantToolCalls: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := &Parser{}
|
|
|
|
var thinkValue *api.ThinkValue
|
|
if tt.thinkEnabled {
|
|
thinkValue = &api.ThinkValue{Value: true}
|
|
} else {
|
|
thinkValue = &api.ThinkValue{Value: false}
|
|
}
|
|
|
|
// Define tools for tool call tests
|
|
props := api.NewToolPropertiesMap()
|
|
props.Set("query", api.ToolProperty{Type: api.PropertyType{"string"}})
|
|
tools := []api.Tool{
|
|
{
|
|
Function: api.ToolFunction{
|
|
Name: "search",
|
|
Parameters: api.ToolFunctionParameters{
|
|
Properties: props,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
p.Init(tools, nil, thinkValue)
|
|
|
|
content, thinking, calls, err := p.Add(tt.input, true)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if thinking != tt.wantThinking {
|
|
t.Errorf("thinking = %q, want %q", thinking, tt.wantThinking)
|
|
}
|
|
if content != tt.wantContent {
|
|
t.Errorf("content = %q, want %q", content, tt.wantContent)
|
|
}
|
|
if len(calls) != tt.wantToolCalls {
|
|
t.Errorf("len(calls) = %d, want %d", len(calls), tt.wantToolCalls)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParserToolCall(t *testing.T) {
|
|
p := &Parser{}
|
|
|
|
props := api.NewToolPropertiesMap()
|
|
props.Set("location", api.ToolProperty{Type: api.PropertyType{"string"}})
|
|
props.Set("unit", api.ToolProperty{Type: api.PropertyType{"string"}})
|
|
tools := []api.Tool{
|
|
{
|
|
Function: api.ToolFunction{
|
|
Name: "get_weather",
|
|
Parameters: api.ToolFunctionParameters{
|
|
Properties: props,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Initialize with thinking disabled
|
|
tv := &api.ThinkValue{Value: false}
|
|
p.Init(tools, nil, tv)
|
|
|
|
input := "<tool_call>get_weather<arg_key>location</arg_key><arg_value>San Francisco</arg_value><arg_key>unit</arg_key><arg_value>celsius</arg_value></tool_call>"
|
|
|
|
_, _, calls, err := p.Add(input, true)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(calls) != 1 {
|
|
t.Fatalf("expected 1 tool call, got %d", len(calls))
|
|
}
|
|
|
|
call := calls[0]
|
|
if call.Function.Name != "get_weather" {
|
|
t.Errorf("function name = %q, want %q", call.Function.Name, "get_weather")
|
|
}
|
|
|
|
location, ok := call.Function.Arguments.Get("location")
|
|
if !ok || location != "San Francisco" {
|
|
t.Errorf("location = %v, want %q", location, "San Francisco")
|
|
}
|
|
|
|
unit, ok := call.Function.Arguments.Get("unit")
|
|
if !ok || unit != "celsius" {
|
|
t.Errorf("unit = %v, want %q", unit, "celsius")
|
|
}
|
|
}
|
|
|
|
func TestOverlap(t *testing.T) {
|
|
tests := []struct {
|
|
s string
|
|
tag string
|
|
want int
|
|
}{
|
|
{"hello<", "</think>", 1},
|
|
{"hello</", "</think>", 2},
|
|
{"hello</t", "</think>", 3},
|
|
{"hello</th", "</think>", 4},
|
|
{"hello</thi", "</think>", 5},
|
|
{"hello</thin", "</think>", 6},
|
|
{"hello</think", "</think>", 7},
|
|
{"hello</think>", "</think>", 8}, // Complete tag at end returns full length
|
|
{"hello", "</think>", 0},
|
|
{"", "</think>", 0},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.s+"_"+tt.tag, func(t *testing.T) {
|
|
got := overlap(tt.s, tt.tag)
|
|
if got != tt.want {
|
|
t.Errorf("overlap(%q, %q) = %d, want %d", tt.s, tt.tag, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTrailingWhitespaceLen(t *testing.T) {
|
|
tests := []struct {
|
|
s string
|
|
want int
|
|
}{
|
|
{"hello ", 3},
|
|
{"hello\n\t ", 3},
|
|
{"hello", 0},
|
|
{"", 0},
|
|
{" ", 3},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.s, func(t *testing.T) {
|
|
got := trailingWhitespaceLen(tt.s)
|
|
if got != tt.want {
|
|
t.Errorf("trailingWhitespaceLen(%q) = %d, want %d", tt.s, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|