Compare commits

...

9 Commits

Author SHA1 Message Date
Bruce MacDonald
a5bc4b7c17 Update images_test.go 2024-12-03 15:03:08 -08:00
Bruce MacDonald
1be080403d server: feedback before failing push on uppercase
When a username or model name is uppercase the registry will reject the
push. This is done for file-system compatibility. If we rely on the registry
error on push the message returned is 'file not found', which does not
convey why the push actually failed.
2024-12-03 14:40:23 -08:00
Tigran
55c3efa900 docs: remove extra quote in modelfile.md (#7908) 2024-12-02 09:28:56 -08:00
David Mayboroda
1aedffad93 readme: add minima to community integrations (#7906) 2024-12-02 01:14:47 -08:00
Jeffrey Morgan
ff6c2d6dc8 cmd: don't rely on reading repo file for test (#7898) 2024-11-30 14:12:53 -08:00
Jeffrey Morgan
d543b282a7 server: add warning message for deprecated context field (#7878) 2024-11-30 14:05:50 -08:00
Parth Sareen
5f8051180e Enable index tracking for tools - openai api support (#7888) 2024-11-29 20:00:09 -08:00
Jeffrey Morgan
39e29ae5dd llama: fix typo and formatting in readme (#7876) 2024-11-28 17:27:11 -08:00
TheCookingSenpai
30a9f063c9 readme: add SpaceLlama, YouLama, and DualMind to community integrations (#7216) 2024-11-28 15:16:27 -08:00
11 changed files with 164 additions and 23 deletions

View File

@@ -346,6 +346,9 @@ See the [API documentation](./docs/api.md) for all endpoints.
- [Web management](https://github.com/lemonit-eric-mao/ollama-web-management) (Web management page) - [Web management](https://github.com/lemonit-eric-mao/ollama-web-management) (Web management page)
- [Promptery](https://github.com/promptery/promptery) (desktop client for Ollama.) - [Promptery](https://github.com/promptery/promptery) (desktop client for Ollama.)
- [Ollama App](https://github.com/JHubi1/ollama-app) (Modern and easy-to-use multi-platform client for Ollama) - [Ollama App](https://github.com/JHubi1/ollama-app) (Modern and easy-to-use multi-platform client for Ollama)
- [SpaceLlama](https://github.com/tcsenpai/spacellama) (Firefox and Chrome extension to quickly summarize web pages with ollama in a sidebar)
- [YouLama](https://github.com/tcsenpai/youlama) (Webapp to quickly summarize any YouTube video, supporting Invidious as well)
- [DualMind](https://github.com/tcsenpai/dualmind) (Experimental app allowing two models to talk to each other in the terminal or in a web interface)
- [ollamarama-matrix](https://github.com/h1ddenpr0cess20/ollamarama-matrix) (Ollama chatbot for the Matrix chat protocol) - [ollamarama-matrix](https://github.com/h1ddenpr0cess20/ollamarama-matrix) (Ollama chatbot for the Matrix chat protocol)
- [ollama-chat-app](https://github.com/anan1213095357/ollama-chat-app) (Flutter-based chat app) - [ollama-chat-app](https://github.com/anan1213095357/ollama-chat-app) (Flutter-based chat app)
- [Perfect Memory AI](https://www.perfectmemory.ai/) (Productivity AI assists personalized by what you have seen on your screen, heard and said in the meetings) - [Perfect Memory AI](https://www.perfectmemory.ai/) (Productivity AI assists personalized by what you have seen on your screen, heard and said in the meetings)
@@ -356,6 +359,7 @@ See the [API documentation](./docs/api.md) for all endpoints.
- [Nosia](https://github.com/nosia-ai/nosia) (Easy to install and use RAG platform based on Ollama) - [Nosia](https://github.com/nosia-ai/nosia) (Easy to install and use RAG platform based on Ollama)
- [Witsy](https://github.com/nbonamy/witsy) (An AI Desktop application avaiable for Mac/Windows/Linux) - [Witsy](https://github.com/nbonamy/witsy) (An AI Desktop application avaiable for Mac/Windows/Linux)
- [Abbey](https://github.com/US-Artificial-Intelligence/abbey) (A configurable AI interface server with notebooks, document storage, and YouTube support) - [Abbey](https://github.com/US-Artificial-Intelligence/abbey) (A configurable AI interface server with notebooks, document storage, and YouTube support)
- [Minima](https://github.com/dmayboroda/minima) (RAG with on-premises or fully local workflow)
### Cloud ### Cloud

View File

@@ -146,6 +146,7 @@ type ToolCall struct {
} }
type ToolCallFunction struct { type ToolCallFunction struct {
Index int `json:"index,omitempty"`
Name string `json:"name"` Name string `json:"name"`
Arguments ToolCallFunctionArguments `json:"arguments"` Arguments ToolCallFunctionArguments `json:"arguments"`
} }

View File

@@ -8,7 +8,6 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
@@ -180,18 +179,14 @@ Weigh anchor!
t.Run("license", func(t *testing.T) { t.Run("license", func(t *testing.T) {
var b bytes.Buffer var b bytes.Buffer
license, err := os.ReadFile(filepath.Join("..", "LICENSE")) license := "MIT License\nCopyright (c) Ollama\n"
if err != nil {
t.Fatal(err)
}
if err := showInfo(&api.ShowResponse{ if err := showInfo(&api.ShowResponse{
Details: api.ModelDetails{ Details: api.ModelDetails{
Family: "test", Family: "test",
ParameterSize: "7B", ParameterSize: "7B",
QuantizationLevel: "FP16", QuantizationLevel: "FP16",
}, },
License: string(license), License: license,
}, &b); err != nil { }, &b); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -49,10 +49,10 @@ Advanced parameters (optional):
- `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature` - `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature`
- `system`: system message to (overrides what is defined in the `Modelfile`) - `system`: system message to (overrides what is defined in the `Modelfile`)
- `template`: the prompt template to use (overrides what is defined in the `Modelfile`) - `template`: the prompt template to use (overrides what is defined in the `Modelfile`)
- `context`: the context parameter returned from a previous request to `/generate`, this can be used to keep a short conversational memory
- `stream`: if `false` the response will be returned as a single response object, rather than a stream of objects - `stream`: if `false` the response will be returned as a single response object, rather than a stream of objects
- `raw`: if `true` no formatting will be applied to the prompt. You may choose to use the `raw` parameter if you are specifying a full templated prompt in your request to the API - `raw`: if `true` no formatting will be applied to the prompt. You may choose to use the `raw` parameter if you are specifying a full templated prompt in your request to the API
- `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`) - `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`)
- `context` (deprecated): the context parameter returned from a previous request to `/generate`, this can be used to keep a short conversational memory
#### JSON mode #### JSON mode

View File

@@ -63,7 +63,7 @@ SYSTEM You are Mario from super mario bros, acting as an assistant.
To use this: To use this:
1. Save it as a file (e.g. `Modelfile`) 1. Save it as a file (e.g. `Modelfile`)
2. `ollama create choose-a-model-name -f <location of the file e.g. ./Modelfile>'` 2. `ollama create choose-a-model-name -f <location of the file e.g. ./Modelfile>`
3. `ollama run choose-a-model-name` 3. `ollama run choose-a-model-name`
4. Start using the model! 4. Start using the model!

View File

@@ -105,7 +105,7 @@ make apply-patches
**Pin to new base commit** **Pin to new base commit**
To update to a newer base commit, select the upstream git tag or commit and update `llama/vendoring.env` To update to a newer base commit, select the upstream git tag or commit and update `llama/vendoring`
#### Applying patches #### Applying patches

View File

@@ -140,6 +140,7 @@ type CompletionChunk struct {
type ToolCall struct { type ToolCall struct {
ID string `json:"id"` ID string `json:"id"`
Index int `json:"index"`
Type string `json:"type"` Type string `json:"type"`
Function struct { Function struct {
Name string `json:"name"` Name string `json:"name"`
@@ -206,6 +207,7 @@ func toToolCalls(tc []api.ToolCall) []ToolCall {
toolCalls[i].ID = toolCallId() toolCalls[i].ID = toolCallId()
toolCalls[i].Type = "function" toolCalls[i].Type = "function"
toolCalls[i].Function.Name = tc.Function.Name toolCalls[i].Function.Name = tc.Function.Name
toolCalls[i].Index = tc.Function.Index
args, err := json.Marshal(tc.Function.Arguments) args, err := json.Marshal(tc.Function.Arguments)
if err != nil { if err != nil {

View File

@@ -195,7 +195,86 @@ func TestChatMiddleware(t *testing.T) {
Stream: &False, Stream: &False,
}, },
}, },
{
name: "chat handler with streaming tools",
body: `{
"model": "test-model",
"messages": [
{"role": "user", "content": "What's the weather like in Paris?"}
],
"stream": true,
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"required": ["location"],
"properties": {
"location": {
"type": "string",
"description": "The city and state"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
}
}
}
}]
}`,
req: api.ChatRequest{
Model: "test-model",
Messages: []api.Message{
{
Role: "user",
Content: "What's the weather like in Paris?",
},
},
Tools: []api.Tool{
{
Type: "function",
Function: api.ToolFunction{
Name: "get_weather",
Description: "Get the current weather",
Parameters: struct {
Type string `json:"type"`
Required []string `json:"required"`
Properties map[string]struct {
Type string `json:"type"`
Description string `json:"description"`
Enum []string `json:"enum,omitempty"`
} `json:"properties"`
}{
Type: "object",
Required: []string{"location"},
Properties: map[string]struct {
Type string `json:"type"`
Description string `json:"description"`
Enum []string `json:"enum,omitempty"`
}{
"location": {
Type: "string",
Description: "The city and state",
},
"unit": {
Type: "string",
Enum: []string{"celsius", "fahrenheit"},
},
},
},
},
},
},
Options: map[string]any{
"temperature": 1.0,
"top_p": 1.0,
},
Stream: &True,
},
},
{ {
name: "chat handler error forwarding", name: "chat handler error forwarding",
body: `{ body: `{

View File

@@ -802,6 +802,12 @@ func PushModel(ctx context.Context, name string, regOpts *registryOptions, fn fu
if mp.ProtocolScheme == "http" && !regOpts.Insecure { if mp.ProtocolScheme == "http" && !regOpts.Insecure {
return errors.New("insecure protocol http") return errors.New("insecure protocol http")
} }
if mp.Namespace != strings.ToLower(mp.Namespace) {
return fmt.Errorf("namespace must be lowercase, but is %s", mp.Namespace)
}
if mp.Repository != strings.ToLower(mp.Repository) {
return fmt.Errorf("model name must be lowercase, but is %s", mp.Repository)
}
manifest, _, err := GetManifest(mp) manifest, _, err := GetManifest(mp)
if err != nil { if err != nil {

50
server/images_test.go Normal file
View File

@@ -0,0 +1,50 @@
package server
import (
"context"
"strings"
"testing"
"github.com/ollama/ollama/api"
)
func TestPushModel(t *testing.T) {
noOpProgress := func(resp api.ProgressResponse) {}
tests := []struct {
modelStr string
regOpts *registryOptions
wantErr string
}{
{
modelStr: "http://example.com/namespace/repo:tag",
regOpts: &registryOptions{Insecure: false},
wantErr: "insecure protocol http",
},
{
modelStr: "docker://Example/repo:tag",
regOpts: &registryOptions{},
wantErr: "namespace must be lowercase, but is Example",
},
{
modelStr: "docker://example/Repo:tag",
regOpts: &registryOptions{},
wantErr: "model name must be lowercase, but is Repo",
},
}
for _, tt := range tests {
t.Run(tt.modelStr, func(t *testing.T) {
err := PushModel(context.Background(), tt.modelStr, tt.regOpts, noOpProgress)
if tt.wantErr != "" {
if err == nil {
t.Errorf("PushModel() error = %v, wantErr %v", err, tt.wantErr)
} else if !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("PushModel() error = %v, wantErr %v", err, tt.wantErr)
}
return
}
})
}
}

View File

@@ -251,6 +251,7 @@ func (s *Server) GenerateHandler(c *gin.Context) {
var b bytes.Buffer var b bytes.Buffer
if req.Context != nil { if req.Context != nil {
slog.Warn("the context field is deprecated and will be removed in a future version of Ollama")
s, err := r.Detokenize(c.Request.Context(), req.Context) s, err := r.Detokenize(c.Request.Context(), req.Context)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
@@ -1469,7 +1470,7 @@ func (s *Server) ChatHandler(c *gin.Context) {
go func() { go func() {
defer close(ch) defer close(ch)
var sb strings.Builder var sb strings.Builder
var hasToolCalls bool var toolCallIndex int = 0
if err := r.Completion(c.Request.Context(), llm.CompletionRequest{ if err := r.Completion(c.Request.Context(), llm.CompletionRequest{
Prompt: prompt, Prompt: prompt,
Images: images, Images: images,
@@ -1509,16 +1510,19 @@ func (s *Server) ChatHandler(c *gin.Context) {
sb.WriteString(r.Content) sb.WriteString(r.Content)
if toolCalls, ok := m.parseToolCalls(sb.String()); ok { if toolCalls, ok := m.parseToolCalls(sb.String()); ok {
res.Message.ToolCalls = toolCalls res.Message.ToolCalls = toolCalls
for i := range toolCalls {
toolCalls[i].Function.Index = toolCallIndex
toolCallIndex++
}
res.Message.Content = "" res.Message.Content = ""
sb.Reset() sb.Reset()
hasToolCalls = true
ch <- res ch <- res
return return
} }
if r.Done { if r.Done {
// Send any remaining content if no tool calls were detected // Send any remaining content if no tool calls were detected
if !hasToolCalls { if toolCallIndex == 0 {
res.Message.Content = sb.String() res.Message.Content = sb.String()
} }
ch <- res ch <- res