From 2e3fd86d482cb4e77e54179836ddd6a518e2300b Mon Sep 17 00:00:00 2001 From: Marcelo Fornet Date: Wed, 16 Jul 2025 19:50:46 +0200 Subject: [PATCH 01/11] docs: fix typo in macos.md (#11425) --- docs/macos.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/macos.md b/docs/macos.md index 63bf14b12..9617bdc74 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -22,7 +22,7 @@ To install the Ollama application somewhere other than `Applications`, place the Ollama on MacOS stores files in a few different locations. - `~/.ollama` contains models and configuration - `~/.ollama/logs` contains logs - - *app.log* contains most resent logs from the GUI application + - *app.log* contains most recent logs from the GUI application - *server.log* contains the most recent server logs - `/Ollama.app/Contents/Resources/ollama` the CLI binary @@ -39,4 +39,4 @@ rm -rf ~/Library/Caches/com.electron.ollama/ rm -rf ~/Library/Caches/ollama rm -rf ~/Library/WebKit/com.electron.ollama rm -rf ~/.ollama -``` \ No newline at end of file +``` From 92c2e8a56c7eb9a5a99439133220d707710da0f8 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Wed, 16 Jul 2025 11:03:28 -0700 Subject: [PATCH 02/11] api: fix unreachable status err (#11423) StatusError was unreachable, the client always checked for error messages in the response body first, and the server always includes error messages with HTTP error status codes. --- api/client.go | 8 ++++---- api/client_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/api/client.go b/api/client.go index 9f0dba8dc..7cc2acb3d 100644 --- a/api/client.go +++ b/api/client.go @@ -222,10 +222,6 @@ func (c *Client) stream(ctx context.Context, method, path string, data any, fn f return fmt.Errorf("unmarshal: %w", err) } - if errorResponse.Error != "" { - return errors.New(errorResponse.Error) - } - if response.StatusCode >= http.StatusBadRequest { return StatusError{ StatusCode: response.StatusCode, @@ -234,6 +230,10 @@ func (c *Client) stream(ctx context.Context, method, path string, data any, fn f } } + if errorResponse.Error != "" { + return errors.New(errorResponse.Error) + } + if err := fn(bts); err != nil { return err } diff --git a/api/client_test.go b/api/client_test.go index 2ceeec9cf..f0034e02d 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -89,6 +89,16 @@ func TestClientStream(t *testing.T) { }, wantErr: "mid-stream error", }, + { + name: "http status error takes precedence over general error", + responses: []any{ + testError{ + message: "custom error message", + statusCode: http.StatusInternalServerError, + }, + }, + wantErr: "500", + }, { name: "successful stream completion", responses: []any{ From d73f8aa8c3979b33f5ea19b80406c20e88ee3b1b Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Wed, 16 Jul 2025 11:18:16 -0700 Subject: [PATCH 03/11] cmd: add default assistant role to message construction (#11431) --- cmd/cmd.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index b569ddddc..c661df4e7 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1080,10 +1080,11 @@ func chat(cmd *cobra.Command, opts runOptions) (*api.Message, error) { var state *displayResponseState = &displayResponseState{} var latest api.ChatResponse var fullResponse strings.Builder - var role string var thinkTagOpened bool = false var thinkTagClosed bool = false + role := "assistant" + fn := func(response api.ChatResponse) error { if response.Message.Content != "" || !opts.HideThinking { p.StopAndClear() From b4fe3adc0a97c160a6af71e7a2c49ceb31a8177c Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Wed, 16 Jul 2025 17:32:57 -0700 Subject: [PATCH 04/11] compile bf16 support into ggml-metal (#11430) --- ml/backend/ggml/ggml/src/ggml-metal/metal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/backend/ggml/ggml/src/ggml-metal/metal.go b/ml/backend/ggml/ggml/src/ggml-metal/metal.go index 0ee017dd1..bf20ab7f9 100644 --- a/ml/backend/ggml/ggml/src/ggml-metal/metal.go +++ b/ml/backend/ggml/ggml/src/ggml-metal/metal.go @@ -4,6 +4,6 @@ package metal //go:generate sh -c "{ echo // Code generated by 'go generate'. DO NOT EDIT.; sed -e '/__embed_ggml-common.h__/r ../ggml-common.h' -e '/__embed_ggml-common.h__/d' -e '/#include \"ggml-metal-impl.h\"/r ggml-metal-impl.h' -e '/#include \"ggml-metal-impl.h\"/d' ggml-metal.metal; } >ggml-metal-embed.metal" -// #cgo CPPFLAGS: -DGGML_METAL_NDEBUG -DGGML_METAL_EMBED_LIBRARY -I.. -I../../include +// #cgo CPPFLAGS: -DGGML_METAL_NDEBUG -DGGML_METAL_EMBED_LIBRARY -DGGML_METAL_USE_BF16 -I.. -I../../include // #cgo LDFLAGS: -framework Metal -framework MetalKit import "C" From e840ccb5239c92f5f118fbdcb3288f844c4a9f8d Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Thu, 17 Jul 2025 12:20:28 +0800 Subject: [PATCH 05/11] readme: update the llama.cpp github link (#11427) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ecbf4a71..7f5d4fb12 100644 --- a/README.md +++ b/README.md @@ -598,7 +598,7 @@ See the [API documentation](./docs/api.md) for all endpoints. ### Supported backends -- [llama.cpp](https://github.com/ggerganov/llama.cpp) project founded by Georgi Gerganov. +- [llama.cpp](https://github.com/ggml-org/llama.cpp) project founded by Georgi Gerganov. ### Observability - [Opik](https://www.comet.com/docs/opik/cookbook/ollama) is an open-source platform to debug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards. Opik supports native intergration to Ollama. From 5e67f4f90e13ce19eb103216bd151ce9f5fb9008 Mon Sep 17 00:00:00 2001 From: frob Date: Thu, 17 Jul 2025 12:31:49 +0800 Subject: [PATCH 06/11] openai: allow openai endpoint to accept webp images (#11412) Co-authored-by: Richard Lyons --- openai/openai.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/openai.go b/openai/openai.go index 012189d23..35b8b9a01 100644 --- a/openai/openai.go +++ b/openai/openai.go @@ -423,7 +423,7 @@ func fromChatRequest(r ChatCompletionRequest) (*api.ChatRequest, error) { } } - types := []string{"jpeg", "jpg", "png"} + types := []string{"jpeg", "jpg", "png", "webp"} valid := false for _, t := range types { prefix := "data:image/" + t + ";base64," From 802ad16ce44312826526d9c6fa4374488a9f4e6c Mon Sep 17 00:00:00 2001 From: frob Date: Thu, 17 Jul 2025 15:16:10 +1000 Subject: [PATCH 07/11] docs: add the no-Modelfile function of `ollama create` (#9077) --- cmd/cmd.go | 4 ++-- docs/import.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index c661df4e7..7955012c8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1418,13 +1418,13 @@ func NewCLI() *cobra.Command { createCmd := &cobra.Command{ Use: "create MODEL", - Short: "Create a model from a Modelfile", + Short: "Create a model", Args: cobra.ExactArgs(1), PreRunE: checkServerHeartbeat, RunE: CreateHandler, } - createCmd.Flags().StringP("file", "f", "", "Name of the Modelfile (default \"Modelfile\"") + createCmd.Flags().StringP("file", "f", "", "Name of the Modelfile (default \"Modelfile\")") createCmd.Flags().StringP("quantize", "q", "", "Quantize model to this level (e.g. q4_K_M)") showCmd := &cobra.Command{ diff --git a/docs/import.md b/docs/import.md index df06ce4b3..104b4162c 100644 --- a/docs/import.md +++ b/docs/import.md @@ -53,6 +53,8 @@ FROM /path/to/safetensors/directory If you create the Modelfile in the same directory as the weights, you can use the command `FROM .`. +If you do not create the Modelfile, ollama will act as if there was a Modelfile with the command `FROM .`. + Now run the `ollama create` command from the directory where you created the `Modelfile`: ```shell From 191d94289d016b59c0553b14d299d1bac07a7fcd Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 17 Jul 2025 07:33:44 -0700 Subject: [PATCH 08/11] ci: switch mac builder to arm64 (#11379) The macos-13 is x86, while macos-13-xlarge is arm64 --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4acb283b0..40871e644 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,7 +23,7 @@ jobs: echo GOFLAGS="'-ldflags=-w -s \"-X=github.com/ollama/ollama/version.Version=${GITHUB_REF_NAME#v}\" \"-X=github.com/ollama/ollama/server.mode=release\"'" >>$GITHUB_OUTPUT darwin-build: - runs-on: macos-13 + runs-on: macos-13-xlarge environment: release needs: setup-environment strategy: From 5fc38d042ff53145026e51027c99a35a08c303ee Mon Sep 17 00:00:00 2001 From: zmldndx <375021616@qq.com> Date: Sun, 20 Jul 2025 04:29:38 +0800 Subject: [PATCH 09/11] readme: update argo description to support deep research (#11455) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f5d4fb12..924a6535c 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Tkinter-based client](https://github.com/chyok/ollama-gui) (Python tkinter-based Client for Ollama) - [LLMChat](https://github.com/trendy-design/llmchat) (Privacy focused, 100% local, intuitive all-in-one chat interface) - [Local Multimodal AI Chat](https://github.com/Leon-Sander/Local-Multimodal-AI-Chat) (Ollama-based LLM Chat with support for multiple features, including PDF RAG, voice chat, image-based interactions, and integration with OpenAI.) -- [ARGO](https://github.com/xark-argo/argo) (Locally download and run Ollama and Huggingface models with RAG on Mac/Windows/Linux) +- [ARGO](https://github.com/xark-argo/argo) (Locally download and run Ollama and Huggingface models with RAG and deep research on Mac/Windows/Linux) - [OrionChat](https://github.com/EliasPereirah/OrionChat) - OrionChat is a web interface for chatting with different AI providers - [G1](https://github.com/bklieger-groq/g1) (Prototype of using prompting strategies to improve the LLM's reasoning through o1-like reasoning chains.) - [Web management](https://github.com/lemonit-eric-mao/ollama-web-management) (Web management page) From bdd9d22dfd9798cad0b17e812e251f9af4c30f12 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 20 Jul 2025 14:55:14 -0700 Subject: [PATCH 10/11] tools: fix parsing issue when a tool name is a substring of another (#11456) Co-authored-by: frob --- tools/tools.go | 85 ++++++++++++---- tools/tools_test.go | 242 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 17 deletions(-) diff --git a/tools/tools.go b/tools/tools.go index f883bf284..c149885f6 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -115,21 +115,7 @@ func (p *Parser) findTag() (int, bool) { // parseToolCall finds the next complete tool call in the buffer // incrementing n and advancing the buffer. func (p *Parser) parseToolCall() *api.ToolCall { - var tool *api.Tool - var end int = len(p.buffer) - var i int - - // find tool name - for _, t := range p.tools { - n := t.Function.Name - if i = bytes.Index(p.buffer, []byte(n)); i != -1 { - if i+len(n) < end { - tool = &t - end = i + len(n) - } - } - } - + tool, end := findTool(p.tools, p.buffer) if tool == nil { return nil } @@ -139,10 +125,10 @@ func (p *Parser) parseToolCall() *api.ToolCall { // parsing arguments before the tool name, which may be needed in the future args := map[string]any{} if len(tool.Function.Parameters.Properties) > 0 { + var i int if args, i = findArguments(*tool, p.buffer[end:]); args == nil { return nil } - end += i } @@ -159,9 +145,74 @@ func (p *Parser) parseToolCall() *api.ToolCall { return tc } +// findTool finds the first tool name in the list that matches the +// beginning of the buffer, returning nil if no tool is found +// or if the buffer ends with a partial tool name since we need +// to wait for more data to disambiguate. +// The second return value is the end position of the tool name +// if one is found, otherwise 0. +func findTool(tools []api.Tool, buf []byte) (*api.Tool, int) { + if len(buf) == 0 { + return nil, 0 + } + + // check if buffer ends with a partial tool name + // this prevents matching "get" when seeing "get_weather" + var longest string + for _, t := range tools { + if len(t.Function.Name) > len(longest) { + longest = t.Function.Name + } + } + + // Only check up to longest characters from the end + for i := 1; i <= min(len(buf), len(longest)); i++ { + tail := buf[len(buf)-i:] + for _, t := range tools { + name := []byte(t.Function.Name) + if len(tail) < len(name) && bytes.HasPrefix(name, tail) { + return nil, 0 + } + } + } + + // find first occurrence of the longest tool name + var found *api.Tool + start := -1 + end := -1 + + for i := range tools { + name := []byte(tools[i].Function.Name) + pos := bytes.Index(buf, name) + if pos == -1 { + continue + } + + // Skip if we have a better match already + if start != -1 { + if pos > start { + continue + } + if pos == start && len(name) <= len(found.Function.Name) { + continue + } + } + + found = &tools[i] + start = pos + end = pos + len(name) + } + + if found != nil { + return found, end + } + + return nil, 0 +} + // findArguments returns the first object that appears to be // arguments for the provided tool in the provided buffer, -// returning nil if no arguments are found. +// returning nil if no arguments are found and the end position // TODO (jmorganca): this does not support parsing omitted arguments // objects for functions that have all-optional parameters // e.g. `{"name": "get_conditions", "arguments": {}}` will work but diff --git a/tools/tools_test.go b/tools/tools_test.go index 8418ab6c3..092ae3233 100644 --- a/tools/tools_test.go +++ b/tools/tools_test.go @@ -112,6 +112,81 @@ func TestParser(t *testing.T) { Description: "Say hello", }, }, + { + Type: "function", + Function: api.ToolFunction{ + Name: "say_hello_world", + Description: "Say hello world", + }, + }, + { + Type: "function", + 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]struct { + Type api.PropertyType `json:"type"` + Items any `json:"items,omitempty"` + Description string `json:"description"` + Enum []any `json:"enum,omitempty"` + } `json:"properties"` + }{ + Type: "object", + Properties: map[string]struct { + Type api.PropertyType `json:"type"` + Items any `json:"items,omitempty"` + Description string `json:"description"` + Enum []any `json:"enum,omitempty"` + }{ + "location": { + Type: api.PropertyType{"string"}, + Description: "The location to get the address for", + }, + }, + }, + }, + }, + { + Type: "function", + 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]struct { + Type api.PropertyType `json:"type"` + Items any `json:"items,omitempty"` + Description string `json:"description"` + Enum []any `json:"enum,omitempty"` + } `json:"properties"` + }{ + Type: "object", + Properties: map[string]struct { + Type api.PropertyType `json:"type"` + Items any `json:"items,omitempty"` + Description string `json:"description"` + Enum []any `json:"enum,omitempty"` + }{ + "a": { + Type: api.PropertyType{"string"}, + Description: "The first number to add", + }, + "b": { + Type: api.PropertyType{"string"}, + Description: "The second number to add", + }, + }, + }, + }, + }, } tests := []struct { @@ -629,6 +704,173 @@ func TestParser(t *testing.T) { }, }, }, + { + name: "tool name with collision", + inputs: []string{ + "", + "{", + "\"name\": \"say_hello", + "_world\",", + "}", + "}", + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "say_hello_world", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + }, + }, + { + name: "tool name with collision multiple", + inputs: []string{ + "", + "{", + "\"name\": \"say_hello", + "_world\",", + "}", + "", + "", + "{", + "\"name\": \"say_hello", + "\",", + "}", + "", + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "say_hello_world", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + { + Function: api.ToolCallFunction{ + Index: 1, + Name: "say_hello", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + }, + }, + { + name: "tool name with collision non streaming", + inputs: []string{ + `{"name": "say_hello`, + }, + content: "", + tmpl: qwen, + calls: nil, + }, + { + name: "tool name with collision non streaming multiple", + inputs: []string{ + `{"name": "say_hello"}{"name": "say_hello_world"}`, + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "say_hello", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + { + Function: api.ToolCallFunction{ + Index: 1, + Name: "say_hello_world", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + }, + }, + { + name: "tool name with collision non streaming shorter", + inputs: []string{ + `{"name": "say_hello"}`, + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "say_hello", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + }, + }, + { + name: "tool name with collision non streaming longer", + inputs: []string{ + `{"name": "say_hello_world"}`, + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "say_hello_world", + Arguments: api.ToolCallFunctionArguments{}, + }, + }, + }, + }, + { + name: "tool name with substring of another", + inputs: []string{ + "{", + "\"name\": \"get_address\",", + "\"arguments\": {", + "\"location\": \"London\"", + "}", + "}", + }, + content: "", + tmpl: json, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "get_address", + Arguments: api.ToolCallFunctionArguments{ + "location": "London", + }, + }, + }, + }, + }, + { + name: "tool name with substring of another", + inputs: []string{ + `{"name": "get_address", "arguments": {"location": "London"}}`, + }, + content: "", + tmpl: qwen, + calls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Index: 0, + Name: "get_address", + Arguments: api.ToolCallFunctionArguments{ + "location": "London", + }, + }, + }, + }, + }, } for _, tt := range tests { From 82da19c634b6cb2e72d6d648b278f1a1bfcc1e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20W=C3=A4rting?= Date: Sun, 20 Jul 2025 23:55:47 +0200 Subject: [PATCH 11/11] readme: add GMAI - Gradle Managed to community integrations (#11461) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 924a6535c..1ea24e75f 100644 --- a/README.md +++ b/README.md @@ -595,6 +595,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [SimpleOllamaUnity](https://github.com/HardCodeDev777/SimpleOllamaUnity) (Unity Engine extension for communicating with Ollama in a few lines of code. Also works at runtime) - [UnityCodeLama](https://github.com/HardCodeDev777/UnityCodeLama) (Unity Edtior tool to analyze scripts via Ollama) - [NativeMind](https://github.com/NativeMindBrowser/NativeMindExtension) (Private, on-device AI Assistant, no cloud dependencies) +- [GMAI - Gradle Managed AI](https://gmai.premex.se/) (Gradle plugin for automated Ollama lifecycle management during build phases) ### Supported backends