Compare commits
1 Commits
mxyng/type
...
v0.11.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3450dd52e |
58
api/types.go
58
api/types.go
@@ -12,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ollama/ollama/envconfig"
|
||||
"github.com/ollama/ollama/types"
|
||||
"github.com/ollama/ollama/types/model"
|
||||
)
|
||||
|
||||
@@ -65,7 +64,7 @@ type GenerateRequest struct {
|
||||
Context []int `json:"context,omitempty"`
|
||||
|
||||
// Stream specifies whether the response is streaming; it is true by default.
|
||||
Stream types.Null[bool] `json:"stream,omitempty"`
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
|
||||
// Raw set to true means that no formatting will be applied to the prompt.
|
||||
Raw bool `json:"raw,omitempty"`
|
||||
@@ -106,7 +105,7 @@ type ChatRequest struct {
|
||||
Messages []Message `json:"messages"`
|
||||
|
||||
// Stream enables streaming of returned responses; true by default.
|
||||
Stream types.Null[bool] `json:"stream,omitempty"`
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
|
||||
// Format is the format to return the response in (e.g. "json").
|
||||
Format json.RawMessage `json:"format,omitempty"`
|
||||
@@ -287,16 +286,23 @@ func mapToTypeScriptType(jsonType string) string {
|
||||
}
|
||||
}
|
||||
|
||||
type ToolFunctionParameters struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]ToolProperty `json:"properties"`
|
||||
}
|
||||
|
||||
func (t *ToolFunctionParameters) String() string {
|
||||
bts, _ := json.Marshal(t)
|
||||
return string(bts)
|
||||
}
|
||||
|
||||
type ToolFunction struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Parameters struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]ToolProperty `json:"properties"`
|
||||
} `json:"parameters"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Parameters ToolFunctionParameters `json:"parameters"`
|
||||
}
|
||||
|
||||
func (t *ToolFunction) String() string {
|
||||
@@ -382,7 +388,7 @@ type EmbedRequest struct {
|
||||
// this request.
|
||||
KeepAlive *Duration `json:"keep_alive,omitempty"`
|
||||
|
||||
Truncate types.Null[bool] `json:"truncate,omitempty"`
|
||||
Truncate *bool `json:"truncate,omitempty"`
|
||||
|
||||
// Options lists model-specific options.
|
||||
Options map[string]any `json:"options"`
|
||||
@@ -421,9 +427,9 @@ type EmbeddingResponse struct {
|
||||
|
||||
// CreateRequest is the request passed to [Client.Create].
|
||||
type CreateRequest struct {
|
||||
Model string `json:"model"`
|
||||
Stream types.Null[bool] `json:"stream,omitempty"`
|
||||
Quantize string `json:"quantize,omitempty"`
|
||||
Model string `json:"model"`
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
Quantize string `json:"quantize,omitempty"`
|
||||
|
||||
From string `json:"from,omitempty"`
|
||||
Files map[string]string `json:"files,omitempty"`
|
||||
@@ -487,11 +493,11 @@ type CopyRequest struct {
|
||||
|
||||
// PullRequest is the request passed to [Client.Pull].
|
||||
type PullRequest struct {
|
||||
Model string `json:"model"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
Username string `json:"username"` // Deprecated: ignored
|
||||
Password string `json:"password"` // Deprecated: ignored
|
||||
Stream types.Null[bool] `json:"stream,omitempty"`
|
||||
Model string `json:"model"`
|
||||
Insecure bool `json:"insecure,omitempty"` // Deprecated: ignored
|
||||
Username string `json:"username"` // Deprecated: ignored
|
||||
Password string `json:"password"` // Deprecated: ignored
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
|
||||
// Deprecated: set the model name with Model instead
|
||||
Name string `json:"name"`
|
||||
@@ -508,11 +514,11 @@ type ProgressResponse struct {
|
||||
|
||||
// PushRequest is the request passed to [Client.Push].
|
||||
type PushRequest struct {
|
||||
Model string `json:"model"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
Username string `json:"username"` // Deprecated: ignored
|
||||
Password string `json:"password"` // Deprecated: ignored
|
||||
Stream types.Null[bool] `json:"stream,omitempty"`
|
||||
Model string `json:"model"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
|
||||
// Deprecated: set the model name with Model instead
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -436,3 +436,50 @@ func TestThinking_UnmarshalJSON(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolFunctionParameters_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params ToolFunctionParameters
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "simple object with string property",
|
||||
params: ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Required: []string{"name"},
|
||||
Properties: map[string]ToolProperty{
|
||||
"name": {
|
||||
Type: PropertyType{"string"},
|
||||
Description: "The name of the person",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: `{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"The name of the person"}}}`,
|
||||
},
|
||||
{
|
||||
name: "marshal failure returns empty string",
|
||||
params: ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Defs: func() any {
|
||||
// Create a cycle that will cause json.Marshal to fail
|
||||
type selfRef struct {
|
||||
Self *selfRef
|
||||
}
|
||||
s := &selfRef{}
|
||||
s.Self = s
|
||||
return s
|
||||
}(),
|
||||
Properties: map[string]ToolProperty{},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := test.params.String()
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/types"
|
||||
"github.com/ollama/ollama/types/model"
|
||||
)
|
||||
|
||||
@@ -572,7 +571,7 @@ func fromChatRequest(r ChatCompletionRequest) (*api.ChatRequest, error) {
|
||||
Messages: messages,
|
||||
Format: format,
|
||||
Options: options,
|
||||
Stream: types.NullWithValue(r.Stream),
|
||||
Stream: &r.Stream,
|
||||
Tools: r.Tools,
|
||||
Think: think,
|
||||
}, nil
|
||||
@@ -651,7 +650,7 @@ func fromCompleteRequest(r CompletionRequest) (api.GenerateRequest, error) {
|
||||
Model: r.Model,
|
||||
Prompt: r.Prompt,
|
||||
Options: options,
|
||||
Stream: types.NullWithValue(r.Stream),
|
||||
Stream: &r.Stream,
|
||||
Suffix: r.Suffix,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (s *Server) CreateHandler(c *gin.Context) {
|
||||
ch <- api.ProgressResponse{Status: "success"}
|
||||
}()
|
||||
|
||||
if !r.Stream.Value(true) {
|
||||
if r.Stream != nil && !*r.Stream {
|
||||
waitForStream(c, ch)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
if !req.Stream.Value(true) {
|
||||
if req.Stream != nil && !*req.Stream {
|
||||
var r api.GenerateResponse
|
||||
var sbThinking strings.Builder
|
||||
var sbContent strings.Builder
|
||||
@@ -487,6 +487,12 @@ func (s *Server) EmbedHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
truncate := true
|
||||
|
||||
if req.Truncate != nil && !*req.Truncate {
|
||||
truncate = false
|
||||
}
|
||||
|
||||
var input []string
|
||||
|
||||
switch i := req.Input.(type) {
|
||||
@@ -535,7 +541,6 @@ func (s *Server) EmbedHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
var count int
|
||||
truncate := req.Truncate.Value(true)
|
||||
for i, s := range input {
|
||||
tokens, err := r.Tokenize(c.Request.Context(), s)
|
||||
if err != nil {
|
||||
@@ -696,7 +701,7 @@ func (s *Server) PullHandler(c *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
if !req.Stream.Value(true) {
|
||||
if req.Stream != nil && !*req.Stream {
|
||||
waitForStream(c, ch)
|
||||
return
|
||||
}
|
||||
@@ -751,7 +756,7 @@ func (s *Server) PushHandler(c *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
if !req.Stream.Value(true) {
|
||||
if req.Stream != nil && !*req.Stream {
|
||||
waitForStream(c, ch)
|
||||
return
|
||||
}
|
||||
@@ -1770,7 +1775,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
if !req.Stream.Value(true) {
|
||||
if req.Stream != nil && !*req.Stream {
|
||||
var resp api.ChatResponse
|
||||
var toolCalls []api.ToolCall
|
||||
var sbThinking strings.Builder
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/ollama/ollama/fs/ggml"
|
||||
)
|
||||
|
||||
var stream bool = false
|
||||
|
||||
func createBinFile(t *testing.T, kv map[string]any, ti []*ggml.Tensor) (string, string) {
|
||||
t.Helper()
|
||||
t.Setenv("OLLAMA_MODELS", cmp.Or(os.Getenv("OLLAMA_MODELS"), t.TempDir()))
|
||||
@@ -116,7 +118,7 @@ func TestCreateFromBin(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -146,7 +148,7 @@ func TestCreateFromModel(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -160,7 +162,7 @@ func TestCreateFromModel(t *testing.T) {
|
||||
w = createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: "test2",
|
||||
From: "test",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -190,7 +192,7 @@ func TestCreateRemovesLayers(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ .Prompt }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -211,7 +213,7 @@ func TestCreateRemovesLayers(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ .System }} {{ .Prompt }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -241,7 +243,7 @@ func TestCreateUnsetsSystem(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
System: "Say hi!",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -262,7 +264,7 @@ func TestCreateUnsetsSystem(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
System: "",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -295,7 +297,7 @@ func TestCreateMergeParameters(t *testing.T) {
|
||||
"top_k": 10,
|
||||
"stop": []string{"USER:", "ASSISTANT:"},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -320,7 +322,7 @@ func TestCreateMergeParameters(t *testing.T) {
|
||||
"temperature": 0.6,
|
||||
"top_p": 0.7,
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -379,7 +381,7 @@ func TestCreateMergeParameters(t *testing.T) {
|
||||
"top_p": 0.7,
|
||||
"stop": []string{"<|endoftext|>"},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -439,7 +441,7 @@ func TestCreateReplacesMessages(t *testing.T) {
|
||||
Content: "Oh, my god.",
|
||||
},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -473,7 +475,7 @@ func TestCreateReplacesMessages(t *testing.T) {
|
||||
Content: "A test. And a thumping good one at that, I'd wager.",
|
||||
},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -534,7 +536,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ .System }} {{ .Prompt }}",
|
||||
System: "Say bye!",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -576,7 +578,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ .Prompt",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
@@ -590,7 +592,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ if .Prompt }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
@@ -604,7 +606,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: "{{ Prompt }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
@@ -625,7 +627,7 @@ func TestCreateLicenses(t *testing.T) {
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
License: []string{"MIT", "Apache-2.0"},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -676,7 +678,7 @@ func TestCreateDetectTemplate(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -696,7 +698,7 @@ func TestCreateDetectTemplate(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: "test",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/ollama/ollama/discover"
|
||||
"github.com/ollama/ollama/fs/ggml"
|
||||
"github.com/ollama/ollama/llm"
|
||||
"github.com/ollama/ollama/types"
|
||||
)
|
||||
|
||||
func TestGenerateDebugRenderOnly(t *testing.T) {
|
||||
@@ -54,6 +53,7 @@ func TestGenerateDebugRenderOnly(t *testing.T) {
|
||||
go s.sched.Run(t.Context())
|
||||
|
||||
// Create a test model
|
||||
stream := false
|
||||
_, digest := createBinFile(t, ggml.KV{
|
||||
"general.architecture": "llama",
|
||||
"llama.block_count": uint32(1),
|
||||
@@ -82,7 +82,7 @@ func TestGenerateDebugRenderOnly(t *testing.T) {
|
||||
Model: "test-model",
|
||||
Files: map[string]string{"file.gguf": digest},
|
||||
Template: "{{ .Prompt }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -172,7 +172,7 @@ func TestGenerateDebugRenderOnly(t *testing.T) {
|
||||
}
|
||||
t.Run(tt.name+streamSuffix, func(t *testing.T) {
|
||||
req := tt.request
|
||||
req.Stream = types.NullWithValue(stream)
|
||||
req.Stream = &stream
|
||||
w := createRequest(t, s.GenerateHandler, req)
|
||||
|
||||
if tt.expectDebug {
|
||||
@@ -246,6 +246,7 @@ func TestChatDebugRenderOnly(t *testing.T) {
|
||||
go s.sched.Run(t.Context())
|
||||
|
||||
// Create a test model
|
||||
stream := false
|
||||
_, digest := createBinFile(t, ggml.KV{
|
||||
"general.architecture": "llama",
|
||||
"llama.block_count": uint32(1),
|
||||
@@ -274,7 +275,7 @@ func TestChatDebugRenderOnly(t *testing.T) {
|
||||
Model: "test-model",
|
||||
Files: map[string]string{"file.gguf": digest},
|
||||
Template: "{{ if .Tools }}{{ .Tools }}{{ end }}{{ range .Messages }}{{ .Role }}: {{ .Content }}\n{{ end }}",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -376,7 +377,7 @@ func TestChatDebugRenderOnly(t *testing.T) {
|
||||
}
|
||||
t.Run(tt.name+streamSuffix, func(t *testing.T) {
|
||||
req := tt.request
|
||||
req.Stream = types.NullWithValue(stream)
|
||||
req.Stream = &stream
|
||||
w := createRequest(t, s.ChatHandler, req)
|
||||
|
||||
if tt.expectDebug {
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
{{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
|
||||
{{- end }}
|
||||
{{ end }}`,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -182,7 +182,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Model: "bert",
|
||||
Files: map[string]string{"bert.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -288,7 +288,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
Messages: []api.Message{
|
||||
{Role: "user", Content: "Hello!"},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -318,7 +318,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
Messages: []api.Message{
|
||||
{Role: "user", Content: "Hello!"},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -340,7 +340,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
{Role: "system", Content: "You can perform magic tricks."},
|
||||
{Role: "user", Content: "Hello!"},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -363,7 +363,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
{Role: "system", Content: "You can perform magic tricks."},
|
||||
{Role: "user", Content: "Help me write tests."},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -422,13 +422,15 @@ func TestGenerateChat(t *testing.T) {
|
||||
EvalDuration: 1,
|
||||
}
|
||||
|
||||
streamRequest := true
|
||||
|
||||
w := createRequest(t, s.ChatHandler, api.ChatRequest{
|
||||
Model: "test-system",
|
||||
Messages: []api.Message{
|
||||
{Role: "user", Content: "What's the weather in Seattle?"},
|
||||
},
|
||||
Tools: tools,
|
||||
Stream: streamTrue,
|
||||
Stream: &streamRequest,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -549,7 +551,7 @@ func TestGenerateChat(t *testing.T) {
|
||||
{Role: "user", Content: "What's the weather in Seattle?"},
|
||||
},
|
||||
Tools: tools,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
@@ -664,7 +666,7 @@ func TestGenerate(t *testing.T) {
|
||||
{{- if .Prompt }}User: {{ .Prompt }} {{ end }}
|
||||
{{- if .Response }}Assistant: {{ .Response }} {{ end }}
|
||||
`,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -702,7 +704,7 @@ func TestGenerate(t *testing.T) {
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Model: "bert",
|
||||
Files: map[string]string{"file.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -823,7 +825,7 @@ func TestGenerate(t *testing.T) {
|
||||
w := createRequest(t, s.GenerateHandler, api.GenerateRequest{
|
||||
Model: "test",
|
||||
Prompt: "Hello!",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -851,7 +853,7 @@ func TestGenerate(t *testing.T) {
|
||||
w := createRequest(t, s.GenerateHandler, api.GenerateRequest{
|
||||
Model: "test-system",
|
||||
Prompt: "Hello!",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -871,7 +873,7 @@ func TestGenerate(t *testing.T) {
|
||||
Model: "test-system",
|
||||
Prompt: "Hello!",
|
||||
System: "You can perform magic tricks.",
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -893,7 +895,7 @@ func TestGenerate(t *testing.T) {
|
||||
Template: `{{- if .System }}{{ .System }} {{ end }}
|
||||
{{- if .Prompt }}### USER {{ .Prompt }} {{ end }}
|
||||
{{- if .Response }}### ASSISTANT {{ .Response }} {{ end }}`,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -955,7 +957,7 @@ func TestGenerate(t *testing.T) {
|
||||
Model: "test-system",
|
||||
Prompt: "Help me write tests.",
|
||||
Raw: true,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -1038,7 +1040,7 @@ func TestChatWithPromptEndingInThinkTag(t *testing.T) {
|
||||
{{- if eq .Role "user" }}user: {{ .Content }}
|
||||
{{ else if eq .Role "assistant" }}assistant: {{ if .Thinking }}<think>{{ .Thinking }}</think>{{ end }}{{ .Content }}
|
||||
{{ end }}{{ end }}<think>`,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -1064,12 +1066,13 @@ func TestChatWithPromptEndingInThinkTag(t *testing.T) {
|
||||
}
|
||||
mock.CompletionFn = nil
|
||||
|
||||
streamRequest := false
|
||||
req := api.ChatRequest{
|
||||
Model: "test-thinking",
|
||||
Messages: []api.Message{
|
||||
{Role: "user", Content: userContent},
|
||||
},
|
||||
Stream: streamFalse,
|
||||
Stream: &streamRequest,
|
||||
}
|
||||
if think {
|
||||
req.Think = &api.ThinkValue{Value: think}
|
||||
@@ -1162,7 +1165,7 @@ func TestChatWithPromptEndingInThinkTag(t *testing.T) {
|
||||
Model: "test-thinking",
|
||||
Messages: []api.Message{{Role: "user", Content: "Analyze this complex problem"}},
|
||||
Think: &api.ThinkValue{Value: think},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
|
||||
@@ -291,11 +291,12 @@ func TestChatHarmonyParserStreamingRealtime(t *testing.T) {
|
||||
// Create a simple test model
|
||||
_, digest := createHarmonyTestModel(t)
|
||||
|
||||
streamFalse := false
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Model: "harmony-test-streaming",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: `<|start|><|end|>{{ with .Tools }}{{ end }}{{ .Prompt }}`,
|
||||
Stream: streamFalse,
|
||||
Stream: &streamFalse,
|
||||
})
|
||||
|
||||
if w.Code != 200 {
|
||||
@@ -303,10 +304,11 @@ func TestChatHarmonyParserStreamingRealtime(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test chat endpoint with streaming
|
||||
streamTrue := true
|
||||
w = createRequest(t, s.ChatHandler, api.ChatRequest{
|
||||
Model: "harmony-test-streaming",
|
||||
Messages: []api.Message{{Role: "user", Content: "Hello"}},
|
||||
Stream: streamTrue,
|
||||
Stream: &streamTrue,
|
||||
Tools: getTestTools(),
|
||||
})
|
||||
|
||||
@@ -439,11 +441,12 @@ func TestChatHarmonyParserStreamingSimple(t *testing.T) {
|
||||
|
||||
// Create model
|
||||
_, digest := createHarmonyTestModel(t)
|
||||
streamFalse := false
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Model: "gpt-oss",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Template: `<|start|><|end|>{{ .Tools }}{{ .Prompt }}`,
|
||||
Stream: streamFalse,
|
||||
Stream: &streamFalse,
|
||||
})
|
||||
|
||||
if w.Code != 200 {
|
||||
@@ -451,10 +454,11 @@ func TestChatHarmonyParserStreamingSimple(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test streaming
|
||||
streamTrue := true
|
||||
w = createRequest(t, s.ChatHandler, api.ChatRequest{
|
||||
Model: "gpt-oss",
|
||||
Messages: []api.Message{{Role: "user", Content: "Hello"}},
|
||||
Stream: streamTrue,
|
||||
Stream: &streamTrue,
|
||||
Tools: getTestTools(),
|
||||
})
|
||||
|
||||
@@ -621,11 +625,12 @@ func TestChatHarmonyParserStreaming(t *testing.T) {
|
||||
_, digest := createHarmonyTestModel(t)
|
||||
|
||||
// Create model with passthrough template
|
||||
stream := false
|
||||
w := createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Model: "harmony-test",
|
||||
Files: map[string]string{"file.gguf": digest},
|
||||
Template: `<|start|><|end|>{{ with .Tools }}{{ end }}{{ .Prompt }}`,
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
})
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
@@ -633,10 +638,11 @@ func TestChatHarmonyParserStreaming(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test chat endpoint with streaming
|
||||
streamTrue := true
|
||||
w = createRequest(t, s.ChatHandler, api.ChatRequest{
|
||||
Model: "harmony-test",
|
||||
Messages: []api.Message{{Role: "user", Content: "Hello"}},
|
||||
Stream: streamTrue,
|
||||
Stream: &streamTrue,
|
||||
Tools: getTestTools(),
|
||||
})
|
||||
|
||||
|
||||
@@ -28,16 +28,10 @@ import (
|
||||
"github.com/ollama/ollama/fs/ggml"
|
||||
"github.com/ollama/ollama/openai"
|
||||
"github.com/ollama/ollama/server/internal/client/ollama"
|
||||
"github.com/ollama/ollama/types"
|
||||
"github.com/ollama/ollama/types/model"
|
||||
"github.com/ollama/ollama/version"
|
||||
)
|
||||
|
||||
var (
|
||||
streamFalse = types.NullWithValue(false)
|
||||
streamTrue = types.NullWithValue(true)
|
||||
)
|
||||
|
||||
func createTestFile(t *testing.T, name string) (string, string) {
|
||||
t.Helper()
|
||||
|
||||
@@ -338,10 +332,11 @@ func TestRoutes(t *testing.T) {
|
||||
Path: "/api/create",
|
||||
Setup: func(t *testing.T, req *http.Request) {
|
||||
_, digest := createTestFile(t, "ollama-model")
|
||||
stream := false
|
||||
createReq := api.CreateRequest{
|
||||
Name: "t-bone",
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
}
|
||||
jsonData, err := json.Marshal(createReq)
|
||||
if err != nil {
|
||||
@@ -643,7 +638,7 @@ func TestManifestCaseSensitivity(t *testing.T) {
|
||||
// version.
|
||||
Name: wantStableName,
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
}))
|
||||
checkManifestList()
|
||||
|
||||
@@ -651,14 +646,14 @@ func TestManifestCaseSensitivity(t *testing.T) {
|
||||
checkOK(createRequest(t, s.CreateHandler, api.CreateRequest{
|
||||
Name: name(),
|
||||
Files: map[string]string{"test.gguf": digest},
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
}))
|
||||
checkManifestList()
|
||||
|
||||
t.Logf("pulling")
|
||||
checkOK(createRequest(t, s.PullHandler, api.PullRequest{
|
||||
Name: name(),
|
||||
Stream: streamFalse,
|
||||
Stream: &stream,
|
||||
Insecure: true,
|
||||
}))
|
||||
checkManifestList()
|
||||
|
||||
@@ -41,13 +41,7 @@ func TestParser(t *testing.T) {
|
||||
Function: api.ToolFunction{
|
||||
Name: "get_temperature",
|
||||
Description: "Retrieve the temperature for a given location",
|
||||
Parameters: struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]api.ToolProperty `json:"properties"`
|
||||
}{
|
||||
Parameters: api.ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Required: []string{"city"},
|
||||
Properties: map[string]api.ToolProperty{
|
||||
@@ -69,13 +63,7 @@ func TestParser(t *testing.T) {
|
||||
Function: api.ToolFunction{
|
||||
Name: "get_conditions",
|
||||
Description: "Retrieve the current weather conditions for a given location",
|
||||
Parameters: struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]api.ToolProperty `json:"properties"`
|
||||
}{
|
||||
Parameters: api.ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]api.ToolProperty{
|
||||
"location": {
|
||||
@@ -105,13 +93,7 @@ func TestParser(t *testing.T) {
|
||||
Function: api.ToolFunction{
|
||||
Name: "get_address",
|
||||
Description: "Get the address of a given location",
|
||||
Parameters: struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]api.ToolProperty `json:"properties"`
|
||||
}{
|
||||
Parameters: api.ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]api.ToolProperty{
|
||||
"location": {
|
||||
@@ -127,13 +109,7 @@ func TestParser(t *testing.T) {
|
||||
Function: api.ToolFunction{
|
||||
Name: "add",
|
||||
Description: "Add two numbers",
|
||||
Parameters: struct {
|
||||
Type string `json:"type"`
|
||||
Defs any `json:"$defs,omitempty"`
|
||||
Items any `json:"items,omitempty"`
|
||||
Required []string `json:"required"`
|
||||
Properties map[string]api.ToolProperty `json:"properties"`
|
||||
}{
|
||||
Parameters: api.ToolFunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]api.ToolProperty{
|
||||
"a": {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Null represents a value of any type T that may be null.
|
||||
type Null[T any] struct {
|
||||
value T
|
||||
valid bool
|
||||
}
|
||||
|
||||
// NullWithValue creates a new, valid Null[T].
|
||||
func NullWithValue[T any](value T) Null[T] {
|
||||
return Null[T]{value: value, valid: true}
|
||||
}
|
||||
|
||||
// Value returns the value of the Type[T] if set, otherwise it returns the provided default value or the zero value of T.
|
||||
func (n Null[T]) Value(defaultValue ...T) T {
|
||||
if n.valid {
|
||||
return n.value
|
||||
}
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
}
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
// SetValue sets the value of the Type[T].
|
||||
func (n *Null[T]) SetValue(t T) {
|
||||
n.value = t
|
||||
n.valid = true
|
||||
}
|
||||
|
||||
// MarshalJSON implements [json.Marshaler].
|
||||
func (n Null[T]) MarshalJSON() ([]byte, error) {
|
||||
if n.valid {
|
||||
return json.Marshal(n.value)
|
||||
}
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements [json.Unmarshaler].
|
||||
func (n *Null[T]) UnmarshalJSON(data []byte) error {
|
||||
if string(data) != "null" {
|
||||
if err := json.Unmarshal(data, &n.value); err != nil {
|
||||
return err
|
||||
}
|
||||
n.valid = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/ollama/ollama/types"
|
||||
)
|
||||
|
||||
func TestNull(t *testing.T) {
|
||||
var s types.Null[string]
|
||||
if val := s.Value(); val != "" {
|
||||
t.Errorf("expected Value to return zero value '', got '%s'", val)
|
||||
}
|
||||
|
||||
if val := s.Value("default"); val != "default" {
|
||||
t.Errorf("expected Value to return default value 'default', got '%s'", val)
|
||||
}
|
||||
|
||||
if bts, err := json.Marshal(s); err != nil {
|
||||
t.Errorf("unexpected error during MarshalJSON: %v", err)
|
||||
} else if want := "null"; string(bts) != want {
|
||||
t.Errorf("expected marshaled JSON to be %s, got %s", want, string(bts))
|
||||
}
|
||||
|
||||
s.SetValue("foo")
|
||||
if val := s.Value(); val != "foo" {
|
||||
t.Errorf("expected Value to return 'foo', got '%s'", val)
|
||||
}
|
||||
|
||||
s = types.NullValue("bar")
|
||||
if val := s.Value(); val != "bar" {
|
||||
t.Errorf("expected Value to return 'bar', got '%s'", val)
|
||||
}
|
||||
|
||||
if bts, err := json.Marshal(s); err != nil {
|
||||
t.Errorf("unexpected error during MarshalJSON: %v", err)
|
||||
} else if want := `"bar"`; string(bts) != want {
|
||||
t.Errorf("expected marshaled JSON to be %s, got %s", want, string(bts))
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(`null`), &s); err != nil {
|
||||
t.Errorf("unexpected error during UnmarshalJSON: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(`"baz"`), &s); err != nil {
|
||||
t.Errorf("unexpected error during UnmarshalJSON: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(`1.2345`), &s); err == nil {
|
||||
t.Error("expected error during UnmarshalJSON with invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user