From df3802a65fa5876708fa03e0369248bd0ea4b2c4 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 1 Aug 2024 17:22:25 -0700 Subject: [PATCH 001/174] Adjust arm cuda repo paths Ubuntu distros fail to install cuda drivers since aarch64 isn't valid --- scripts/install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index aa8b3e5e..03af5a69 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -209,15 +209,15 @@ install_cuda_driver_yum() { case $PACKAGE_MANAGER in yum) $SUDO $PACKAGE_MANAGER -y install yum-utils - if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-$1$2.repo" >/dev/null ; then - $SUDO $PACKAGE_MANAGER-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-$1$2.repo + if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-$1$2.repo" >/dev/null ; then + $SUDO $PACKAGE_MANAGER-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-$1$2.repo else error $CUDA_REPO_ERR_MSG fi ;; dnf) - if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-$1$2.repo" >/dev/null ; then - $SUDO $PACKAGE_MANAGER config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-$1$2.repo + if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-$1$2.repo" >/dev/null ; then + $SUDO $PACKAGE_MANAGER config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-$1$2.repo else error $CUDA_REPO_ERR_MSG fi @@ -245,8 +245,8 @@ install_cuda_driver_yum() { # ref: https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#debian install_cuda_driver_apt() { status 'Installing NVIDIA repository...' - if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-keyring_1.1-1_all.deb" >/dev/null ; then - curl -fsSL -o $TEMP_DIR/cuda-keyring.deb https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m)/cuda-keyring_1.1-1_all.deb + if curl -I --silent --fail --location "https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-keyring_1.1-1_all.deb" >/dev/null ; then + curl -fsSL -o $TEMP_DIR/cuda-keyring.deb https://developer.download.nvidia.com/compute/cuda/repos/$1$2/$(uname -m | sed -e 's/aarch64/sbsa/')/cuda-keyring_1.1-1_all.deb else error $CUDA_REPO_ERR_MSG fi From ed6c8bfe57e4678090b89fc8f6c4e08ce1b01040 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Mon, 5 Aug 2024 00:02:47 -0700 Subject: [PATCH 002/174] removeall to remove non-empty temp dirs --- gpu/assets.go | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/gpu/assets.go b/gpu/assets.go index a35b6630..1c33b55b 100644 --- a/gpu/assets.go +++ b/gpu/assets.go @@ -67,37 +67,44 @@ func PayloadsDir() (string, error) { // Best effort to clean up prior tmpdirs func cleanupTmpDirs() { - dirs, err := filepath.Glob(filepath.Join(os.TempDir(), "ollama*")) + matches, err := filepath.Glob(filepath.Join(os.TempDir(), "ollama*", "ollama.pid")) if err != nil { return } - for _, d := range dirs { - info, err := os.Stat(d) - if err != nil || !info.IsDir() { + + for _, match := range matches { + raw, err := os.ReadFile(match) + if errors.Is(err, os.ErrNotExist) { + slog.Debug("not a ollama runtime directory, skipping", "path", match) continue - } - raw, err := os.ReadFile(filepath.Join(d, "ollama.pid")) - if err != nil { - slog.Warn("failed to read ollama.pid", "path", d, "error", err) - // No pid, ignore this tmpdir + } else if err != nil { + slog.Warn("could not read ollama.pid, skipping", "path", match, "error", err) continue } pid, err := strconv.Atoi(string(raw)) if err != nil { - slog.Warn("failed to parse pid", "path", d, "error", err) + slog.Warn("invalid pid, skipping", "path", match, "error", err) continue } - proc, err := os.FindProcess(pid) - if err == nil && !errors.Is(proc.Signal(syscall.Signal(0)), os.ErrProcessDone) { - slog.Warn("found running ollama", "pid", pid, "path", d) - // Another running ollama, ignore this tmpdir + p, err := os.FindProcess(pid) + if err == nil && !errors.Is(p.Signal(syscall.Signal(0)), os.ErrProcessDone) { + slog.Warn("process still running, skipping", "pid", pid, "path", match) continue } - if err := os.Remove(d); err != nil { - slog.Warn("unable to cleanup stale tmpdir", "path", d, "error", err) + if err := os.Remove(match); err != nil { + slog.Warn("could not cleanup stale pidfile", "path", match, "error", err) + } + + runners := filepath.Join(filepath.Dir(match), "runners") + if err := os.RemoveAll(runners); err != nil { + slog.Warn("could not cleanup stale runners", "path", runners, "error", err) + } + + if err := os.Remove(filepath.Dir(match)); err != nil { + slog.Warn("could not cleanup stale tmpdir", "path", filepath.Dir(match), "error", err) } } } From 43f9d92008bf1aaa2e89ca50c85761540f70c21a Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Mon, 5 Aug 2024 00:34:09 -0700 Subject: [PATCH 003/174] close pid file --- gpu/assets.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/gpu/assets.go b/gpu/assets.go index 1c33b55b..6d62d0dc 100644 --- a/gpu/assets.go +++ b/gpu/assets.go @@ -49,13 +49,9 @@ func PayloadsDir() (string, error) { } // Track our pid so we can clean up orphaned tmpdirs - pidFilePath := filepath.Join(tmpDir, "ollama.pid") - pidFile, err := os.OpenFile(pidFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm) - if err != nil { - return "", err - } - if _, err := pidFile.Write([]byte(strconv.Itoa(os.Getpid()))); err != nil { - return "", err + n := filepath.Join(tmpDir, "ollama.pid") + if err := os.WriteFile(n, []byte(strconv.Itoa(os.Getpid())), 0o644); err != nil { + return "", fmt.Errorf("failed to write pid file %s: %w", n, err) } // We create a distinct subdirectory for payloads within the tmpdir From 04210aa6ddf9ec5d5b6101f6e8a12b68d7aadfee Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Mon, 5 Aug 2024 09:28:07 -0700 Subject: [PATCH 004/174] Catch one more error log --- llm/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/llm/status.go b/llm/status.go index d9f36115..604fe9e0 100644 --- a/llm/status.go +++ b/llm/status.go @@ -26,6 +26,7 @@ var errorPrefixes = []string{ "cudaMalloc failed", "\"ERR\"", "error loading model", + "GGML_ASSERT", } func (w *StatusWriter) Write(b []byte) (int, error) { From 685a53534b80a14efdfdb09ca00af984782ba6ee Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 1 Aug 2024 15:05:16 -0700 Subject: [PATCH 005/174] manifest: Don't prune layers if we can't open a manifest file If there is an error when opening a manifest file (corrupted, permission denied, etc.) then the referenced layers will not be included in the list of active layers. This causes them to be deleted when pruning happens at startup or a model is pulled. In such a situation, we should prefer to preserve data in the hopes that it can be recovered rather than being agressive about deletion. --- server/images.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/images.go b/server/images.go index 81357f3c..05875a88 100644 --- a/server/images.go +++ b/server/images.go @@ -714,8 +714,7 @@ func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}) // save (i.e. delete from the deleteMap) any files used in other manifests manifest, _, err := GetManifest(fmp) if err != nil { - //nolint:nilerr - return nil + return err } for _, layer := range manifest.Layers { @@ -782,7 +781,8 @@ func PruneLayers() error { err = deleteUnusedLayers(nil, deleteMap) if err != nil { - return err + slog.Info(fmt.Sprintf("couldn't remove unused layers: %v", err)) + return nil } slog.Info(fmt.Sprintf("total unused blobs removed: %d", len(deleteMap))) @@ -971,7 +971,8 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu fn(api.ProgressResponse{Status: "removing any unused layers"}) err = deleteUnusedLayers(nil, deleteMap) if err != nil { - return err + slog.Info(fmt.Sprintf("couldn't remove unused layers: %v", err)) + fn(api.ProgressResponse{Status: fmt.Sprintf("couldn't remove unused layers: %v", err)}) } } From ce67706037a2583157fcac4cbf6253fe0f1e5139 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Wed, 7 Aug 2024 18:15:17 +0200 Subject: [PATCH 006/174] Set *.png and *.ico to be treated as binary files. The change b732beba6 makes all files text files and sets lf as eol. This will automatically change all files to have lf if they are touched by git (e.g. via git status). This change cannot be stashed and makes it hard to work with the repo (rebase and checkout don't really work). See also #6183. Here, we set the offending files (*.png and *.ico, but that might be more in the future) to be treated as binary files and not be changed by git. --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index f7192096..648c78ca 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ llm/ext_server/* linguist-vendored * text eol=lf +*.png binary +*.ico binary From 1829fb61bd7a4186881714618f09b2877d0bc9a3 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Mon, 5 Aug 2024 17:13:52 -0700 Subject: [PATCH 007/174] manifest: Fix crash on startup when trying to clean up unused files (#5840) Currently if the config field is missing in the manifest file (or corrupted), Ollama will crash when it tries to read it. This can happen at startup or when pulling new models. This data is mostly just used for showing model information so we can be tolerant of it not being present - it is not required to run the models. Besides avoiding crashing, this also gives us the ability to restructure the config in the future by pulling it into the main manifest file. --- server/images.go | 40 ++++++++++++++++++++++++---------------- server/layer.go | 15 ++++++++++++++- server/manifest.go | 18 ++++++++++-------- server/routes.go | 23 +++++++++++++---------- 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/server/images.go b/server/images.go index 05875a88..7ed35995 100644 --- a/server/images.go +++ b/server/images.go @@ -250,19 +250,21 @@ func GetModel(name string) (*Model, error) { Template: template.DefaultTemplate, } - filename, err := GetBlobsPath(manifest.Config.Digest) - if err != nil { - return nil, err - } + if manifest.Config.Digest != "" { + filename, err := GetBlobsPath(manifest.Config.Digest) + if err != nil { + return nil, err + } - configFile, err := os.Open(filename) - if err != nil { - return nil, err - } - defer configFile.Close() + configFile, err := os.Open(filename) + if err != nil { + return nil, err + } + defer configFile.Close() - if err := json.NewDecoder(configFile).Decode(&model.Config); err != nil { - return nil, err + if err := json.NewDecoder(configFile).Decode(&model.Config); err != nil { + return nil, err + } } for _, layer := range manifest.Layers { @@ -781,7 +783,7 @@ func PruneLayers() error { err = deleteUnusedLayers(nil, deleteMap) if err != nil { - slog.Info(fmt.Sprintf("couldn't remove unused layers: %v", err)) + slog.Error(fmt.Sprintf("couldn't remove unused layers: %v", err)) return nil } @@ -839,7 +841,9 @@ func PushModel(ctx context.Context, name string, regOpts *registryOptions, fn fu var layers []*Layer layers = append(layers, manifest.Layers...) - layers = append(layers, manifest.Config) + if manifest.Config.Digest != "" { + layers = append(layers, &manifest.Config) + } for _, layer := range layers { if err := uploadBlob(ctx, mp, layer, regOpts, fn); err != nil { @@ -890,7 +894,9 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu for _, l := range manifest.Layers { deleteMap[l.Digest] = struct{}{} } - deleteMap[manifest.Config.Digest] = struct{}{} + if manifest.Config.Digest != "" { + deleteMap[manifest.Config.Digest] = struct{}{} + } } } @@ -907,7 +913,9 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu var layers []*Layer layers = append(layers, manifest.Layers...) - layers = append(layers, manifest.Config) + if manifest.Config.Digest != "" { + layers = append(layers, &manifest.Config) + } skipVerify := make(map[string]bool) for _, layer := range layers { @@ -971,7 +979,7 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu fn(api.ProgressResponse{Status: "removing any unused layers"}) err = deleteUnusedLayers(nil, deleteMap) if err != nil { - slog.Info(fmt.Sprintf("couldn't remove unused layers: %v", err)) + slog.Error(fmt.Sprintf("couldn't remove unused layers: %v", err)) fn(api.ProgressResponse{Status: fmt.Sprintf("couldn't remove unused layers: %v", err)}) } } diff --git a/server/layer.go b/server/layer.go index cc6709d2..a2b66782 100644 --- a/server/layer.go +++ b/server/layer.go @@ -2,6 +2,7 @@ package server import ( "crypto/sha256" + "errors" "fmt" "io" "os" @@ -61,6 +62,10 @@ func NewLayer(r io.Reader, mediatype string) (*Layer, error) { } func NewLayerFromLayer(digest, mediatype, from string) (*Layer, error) { + if digest == "" { + return nil, errors.New("creating new layer from layer with empty digest") + } + blob, err := GetBlobsPath(digest) if err != nil { return nil, err @@ -81,6 +86,10 @@ func NewLayerFromLayer(digest, mediatype, from string) (*Layer, error) { } func (l *Layer) Open() (io.ReadSeekCloser, error) { + if l.Digest == "" { + return nil, errors.New("opening layer with empty digest") + } + blob, err := GetBlobsPath(l.Digest) if err != nil { return nil, err @@ -90,13 +99,17 @@ func (l *Layer) Open() (io.ReadSeekCloser, error) { } func (l *Layer) Remove() error { + if l.Digest == "" { + return nil + } + ms, err := Manifests() if err != nil { return err } for _, m := range ms { - for _, layer := range append(m.Layers, m.Config) { + for _, layer := range append(m.Layers, &m.Config) { if layer.Digest == l.Digest { // something is using this layer return nil diff --git a/server/manifest.go b/server/manifest.go index b8df11ef..b966ddbe 100644 --- a/server/manifest.go +++ b/server/manifest.go @@ -16,7 +16,7 @@ import ( type Manifest struct { SchemaVersion int `json:"schemaVersion"` MediaType string `json:"mediaType"` - Config *Layer `json:"config"` + Config Layer `json:"config"` Layers []*Layer `json:"layers"` filepath string @@ -25,7 +25,7 @@ type Manifest struct { } func (m *Manifest) Size() (size int64) { - for _, layer := range append(m.Layers, m.Config) { + for _, layer := range append(m.Layers, &m.Config) { size += layer.Size } @@ -46,11 +46,13 @@ func (m *Manifest) Remove() error { } func (m *Manifest) RemoveLayers() error { - for _, layer := range append(m.Layers, m.Config) { - if err := layer.Remove(); errors.Is(err, os.ErrNotExist) { - slog.Debug("layer does not exist", "digest", layer.Digest) - } else if err != nil { - return err + for _, layer := range append(m.Layers, &m.Config) { + if layer.Digest != "" { + if err := layer.Remove(); errors.Is(err, os.ErrNotExist) { + slog.Debug("layer does not exist", "digest", layer.Digest) + } else if err != nil { + return err + } } } @@ -113,7 +115,7 @@ func WriteManifest(name model.Name, config *Layer, layers []*Layer) error { m := Manifest{ SchemaVersion: 2, MediaType: "application/vnd.docker.distribution.manifest.v2+json", - Config: config, + Config: *config, Layers: layers, } diff --git a/server/routes.go b/server/routes.go index b9c66b65..e55eaa9d 100644 --- a/server/routes.go +++ b/server/routes.go @@ -824,17 +824,20 @@ func (s *Server) ListModelsHandler(c *gin.Context) { models := []api.ListModelResponse{} for n, m := range ms { - f, err := m.Config.Open() - if err != nil { - slog.Warn("bad manifest filepath", "name", n, "error", err) - continue - } - defer f.Close() - var cf ConfigV2 - if err := json.NewDecoder(f).Decode(&cf); err != nil { - slog.Warn("bad manifest config", "name", n, "error", err) - continue + + if m.Config.Digest != "" { + f, err := m.Config.Open() + if err != nil { + slog.Warn("bad manifest filepath", "name", n, "error", err) + continue + } + defer f.Close() + + if err := json.NewDecoder(f).Decode(&cf); err != nil { + slog.Warn("bad manifest config", "name", n, "error", err) + continue + } } // tag should never be masked From ad0c19dde403ba67aa27247775e33c33c30ee235 Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Wed, 7 Aug 2024 14:20:50 -0700 Subject: [PATCH 008/174] Use llama3.1 in tools example (#5985) * Use llama3.1 in tools example * Update api.md --- docs/api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.md b/docs/api.md index c0202ef3..5cbba523 100644 --- a/docs/api.md +++ b/docs/api.md @@ -669,7 +669,7 @@ curl http://localhost:11434/api/chat -d '{ ``` curl http://localhost:11434/api/chat -d '{ - "model": "mistral", + "model": "llama3.1", "messages": [ { "role": "user", @@ -708,7 +708,7 @@ curl http://localhost:11434/api/chat -d '{ ```json { - "model": "mistral:7b-instruct-v0.3-q4_K_M", + "model": "llama3.1", "created_at": "2024-07-22T20:33:28.123648Z", "message": { "role": "assistant", From 5b3a21b578da89b1682a98ce123a6b3c91697e9b Mon Sep 17 00:00:00 2001 From: royjhan <65097070+royjhan@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:43:44 -0400 Subject: [PATCH 009/174] add metrics to docs (#6079) --- docs/api.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 5cbba523..aed2b69f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1175,7 +1175,10 @@ curl http://localhost:11434/api/embed -d '{ "embeddings": [[ 0.010071029, -0.0017594862, 0.05007221, 0.04692972, 0.054916814, 0.008599704, 0.105441414, -0.025878139, 0.12958129, 0.031952348 - ]] + ]], + "total_duration": 14143917, + "load_duration": 1019500, + "prompt_eval_count": 8 } ``` From 97ec8cfd4ef13190f3939fbb24b6f146d570ed12 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Wed, 7 Aug 2024 11:44:25 -0700 Subject: [PATCH 010/174] image: Clarify argument to WriteManifest is config When creating a model the config layer is appended to the list of layers and then the last layer is used as the config when writing the manifest. This change directly uses the config layer to write the manifest. There is no behavior change but it is less error prone. --- server/images.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/images.go b/server/images.go index 7ed35995..4202a413 100644 --- a/server/images.go +++ b/server/images.go @@ -625,12 +625,12 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio return err } - layer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json") + configLayer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json") if err != nil { return err } - for _, layer := range append(layers, layer) { + for _, layer := range append(layers, configLayer) { if layer.status != "" { fn(api.ProgressResponse{Status: layer.status}) } @@ -639,7 +639,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio old, _ := ParseNamedManifest(name) fn(api.ProgressResponse{Status: "writing manifest"}) - if err := WriteManifest(name, layer, layers); err != nil { + if err := WriteManifest(name, configLayer, layers); err != nil { return err } From 7edaf6e7e8d79a9c88419988ae98afaf3fc32f15 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Wed, 7 Aug 2024 14:22:17 -0700 Subject: [PATCH 011/174] manifest: Store layers inside manifests consistently as values. Commit 1829fb61 ("manifest: Fix crash on startup when trying to clean up unused files (#5840)") changed the config layer stored in manifests from a pointer to a value. This was done in order to avoid potential nil pointer dereferences after it is deserialized from JSON in the event that the field is missing. This changes the Layers slice to also be stored by value. This enables consistency in handling across the two objects. --- server/images.go | 14 +++++++------- server/layer.go | 28 ++++++++++++++-------------- server/manifest.go | 16 ++++++++-------- server/model.go | 2 +- server/routes_delete_test.go | 2 +- server/upload.go | 4 ++-- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/server/images.go b/server/images.go index 4202a413..0e753f56 100644 --- a/server/images.go +++ b/server/images.go @@ -373,7 +373,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio var messages []*api.Message parameters := make(map[string]any) - var layers []*Layer + var layers []Layer for _, c := range modelfile.Commands { mediatype := fmt.Sprintf("application/vnd.ollama.image.%s", c.Name) @@ -499,7 +499,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio if c.Name != "license" { // replace - layers = slices.DeleteFunc(layers, func(layer *Layer) bool { + layers = slices.DeleteFunc(layers, func(layer Layer) bool { if layer.MediaType != mediatype { return false } @@ -545,7 +545,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio } var err2 error - layers = slices.DeleteFunc(layers, func(layer *Layer) bool { + layers = slices.DeleteFunc(layers, func(layer Layer) bool { switch layer.MediaType { case "application/vnd.ollama.image.message": // if there are new messages, remove the inherited ones @@ -839,10 +839,10 @@ func PushModel(ctx context.Context, name string, regOpts *registryOptions, fn fu return err } - var layers []*Layer + var layers []Layer layers = append(layers, manifest.Layers...) if manifest.Config.Digest != "" { - layers = append(layers, &manifest.Config) + layers = append(layers, manifest.Config) } for _, layer := range layers { @@ -911,10 +911,10 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu return fmt.Errorf("pull model manifest: %s", err) } - var layers []*Layer + var layers []Layer layers = append(layers, manifest.Layers...) if manifest.Config.Digest != "" { - layers = append(layers, &manifest.Config) + layers = append(layers, manifest.Config) } skipVerify := make(map[string]bool) diff --git a/server/layer.go b/server/layer.go index a2b66782..c666bd10 100644 --- a/server/layer.go +++ b/server/layer.go @@ -16,15 +16,15 @@ type Layer struct { status string } -func NewLayer(r io.Reader, mediatype string) (*Layer, error) { +func NewLayer(r io.Reader, mediatype string) (Layer, error) { blobs, err := GetBlobsPath("") if err != nil { - return nil, err + return Layer{}, err } temp, err := os.CreateTemp(blobs, "sha256-") if err != nil { - return nil, err + return Layer{}, err } defer temp.Close() defer os.Remove(temp.Name()) @@ -32,28 +32,28 @@ func NewLayer(r io.Reader, mediatype string) (*Layer, error) { sha256sum := sha256.New() n, err := io.Copy(io.MultiWriter(temp, sha256sum), r) if err != nil { - return nil, err + return Layer{}, err } if err := temp.Close(); err != nil { - return nil, err + return Layer{}, err } digest := fmt.Sprintf("sha256:%x", sha256sum.Sum(nil)) blob, err := GetBlobsPath(digest) if err != nil { - return nil, err + return Layer{}, err } status := "using existing layer" if _, err := os.Stat(blob); err != nil { status = "creating new layer" if err := os.Rename(temp.Name(), blob); err != nil { - return nil, err + return Layer{}, err } } - return &Layer{ + return Layer{ MediaType: mediatype, Digest: digest, Size: n, @@ -61,22 +61,22 @@ func NewLayer(r io.Reader, mediatype string) (*Layer, error) { }, nil } -func NewLayerFromLayer(digest, mediatype, from string) (*Layer, error) { +func NewLayerFromLayer(digest, mediatype, from string) (Layer, error) { if digest == "" { - return nil, errors.New("creating new layer from layer with empty digest") + return Layer{}, errors.New("creating new layer from layer with empty digest") } blob, err := GetBlobsPath(digest) if err != nil { - return nil, err + return Layer{}, err } fi, err := os.Stat(blob) if err != nil { - return nil, err + return Layer{}, err } - return &Layer{ + return Layer{ MediaType: mediatype, Digest: digest, Size: fi.Size(), @@ -109,7 +109,7 @@ func (l *Layer) Remove() error { } for _, m := range ms { - for _, layer := range append(m.Layers, &m.Config) { + for _, layer := range append(m.Layers, m.Config) { if layer.Digest == l.Digest { // something is using this layer return nil diff --git a/server/manifest.go b/server/manifest.go index b966ddbe..6a5d7b88 100644 --- a/server/manifest.go +++ b/server/manifest.go @@ -14,10 +14,10 @@ import ( ) type Manifest struct { - SchemaVersion int `json:"schemaVersion"` - MediaType string `json:"mediaType"` - Config Layer `json:"config"` - Layers []*Layer `json:"layers"` + SchemaVersion int `json:"schemaVersion"` + MediaType string `json:"mediaType"` + Config Layer `json:"config"` + Layers []Layer `json:"layers"` filepath string fi os.FileInfo @@ -25,7 +25,7 @@ type Manifest struct { } func (m *Manifest) Size() (size int64) { - for _, layer := range append(m.Layers, &m.Config) { + for _, layer := range append(m.Layers, m.Config) { size += layer.Size } @@ -46,7 +46,7 @@ func (m *Manifest) Remove() error { } func (m *Manifest) RemoveLayers() error { - for _, layer := range append(m.Layers, &m.Config) { + for _, layer := range append(m.Layers, m.Config) { if layer.Digest != "" { if err := layer.Remove(); errors.Is(err, os.ErrNotExist) { slog.Debug("layer does not exist", "digest", layer.Digest) @@ -95,7 +95,7 @@ func ParseNamedManifest(n model.Name) (*Manifest, error) { return &m, nil } -func WriteManifest(name model.Name, config *Layer, layers []*Layer) error { +func WriteManifest(name model.Name, config Layer, layers []Layer) error { manifests, err := GetManifestPath() if err != nil { return err @@ -115,7 +115,7 @@ func WriteManifest(name model.Name, config *Layer, layers []*Layer) error { m := Manifest{ SchemaVersion: 2, MediaType: "application/vnd.docker.distribution.manifest.v2+json", - Config: *config, + Config: config, Layers: layers, } diff --git a/server/model.go b/server/model.go index f2946a0b..ad6e4e55 100644 --- a/server/model.go +++ b/server/model.go @@ -26,7 +26,7 @@ import ( var intermediateBlobs map[string]string = make(map[string]string) type layerGGML struct { - *Layer + Layer *llm.GGML } diff --git a/server/routes_delete_test.go b/server/routes_delete_test.go index 1c950d66..82fac9f5 100644 --- a/server/routes_delete_test.go +++ b/server/routes_delete_test.go @@ -98,7 +98,7 @@ func TestDeleteDuplicateLayers(t *testing.T) { } // create a manifest with duplicate layers - if err := WriteManifest(n, config, []*Layer{config}); err != nil { + if err := WriteManifest(n, config, []Layer{config}); err != nil { t.Fatal(err) } diff --git a/server/upload.go b/server/upload.go index b5a244ea..2f115436 100644 --- a/server/upload.go +++ b/server/upload.go @@ -26,7 +26,7 @@ import ( var blobUploadManager sync.Map type blobUpload struct { - *Layer + Layer Total int64 Completed atomic.Int64 @@ -362,7 +362,7 @@ func (p *progressWriter) Rollback() { p.written = 0 } -func uploadBlob(ctx context.Context, mp ModelPath, layer *Layer, opts *registryOptions, fn func(api.ProgressResponse)) error { +func uploadBlob(ctx context.Context, mp ModelPath, layer Layer, opts *registryOptions, fn func(api.ProgressResponse)) error { requestURL := mp.BaseURL() requestURL = requestURL.JoinPath("v2", mp.GetNamespaceRepository(), "blobs", layer.Digest) From 7b61eba47159748bcfc35227a13e31c899a84e49 Mon Sep 17 00:00:00 2001 From: Jitang Lei Date: Thu, 8 Aug 2024 20:28:01 +0800 Subject: [PATCH 012/174] server/download.go: Fix a typo in log Signed-off-by: Jitang Lei --- server/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/download.go b/server/download.go index 38d24a6b..cf31df5e 100644 --- a/server/download.go +++ b/server/download.go @@ -235,7 +235,7 @@ func (b *blobDownload) run(ctx context.Context, requestURL *url.URL, opts *regis newOpts.CheckRedirect = func(req *http.Request, via []*http.Request) error { if len(via) > 10 { - return errors.New("maxium redirects exceeded (10) for directURL") + return errors.New("maximum redirects exceeded (10) for directURL") } // if the hostname is the same, allow the redirect From 2003d601594c89931d4741d20ffa730a14bbb4d9 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 8 Aug 2024 11:18:13 -0700 Subject: [PATCH 013/174] llama3.1 memory --- llm/ggml.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/llm/ggml.go b/llm/ggml.go index d7f2eef7..bde9c347 100644 --- a/llm/ggml.go +++ b/llm/ggml.go @@ -344,11 +344,13 @@ func (llm GGML) GraphSize(context, batch uint64) (partialOffload, fullOffload ui switch llm.KV().Architecture() { case "llama": - fullOffload = 4 * batch * (1 + 4*embedding + context*(1+heads)) + fullOffload = max( + 4*batch*(1+4*embedding+context*(1+heads)), + 4*batch*(embedding+vocab), + ) partialOffload = 4 * batch * embedding partialOffload += max( - // 4*batch*(4+6*embedding+context*(2*heads)+llm.KV().GQA()), 4*batch*(1+embedding+max(context, embedding))+embedding*embedding*9/16+4*context*(batch*heads+embeddingHeads*headsKV), 4*batch*(embedding+vocab)+embedding*vocab*105/128, ) From 67472e0e89f516ccfbfad2d11414aadf484b7642 Mon Sep 17 00:00:00 2001 From: Nicholas42 Date: Fri, 9 Aug 2024 13:41:20 +0200 Subject: [PATCH 014/174] Also flag *.icns as binary --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 648c78ca..baabd3c1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ llm/ext_server/* linguist-vendored * text eol=lf *.png binary *.ico binary +*.icns binary From 5bca2e60a7baefe582077469a1d14ff516b5d322 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 9 Aug 2024 11:31:38 -0700 Subject: [PATCH 015/174] Harden intel boostrap for nil pointers --- gpu/gpu.go | 61 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/gpu/gpu.go b/gpu/gpu.go index 7ae8fbec..dc124a3e 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -305,38 +305,41 @@ func GetGPUInfo() GpuInfoList { // Intel if envconfig.IntelGPU() { oHandles = initOneAPIHandles() - // On windows we bundle the oneapi library one level above the runner dir - depPath = "" - if runtime.GOOS == "windows" && envconfig.RunnersDir() != "" { - depPath = filepath.Join(filepath.Dir(envconfig.RunnersDir()), "oneapi") - } + if oHandles != nil && oHandles.oneapi != nil { - for d := range oHandles.oneapi.num_drivers { - if oHandles.oneapi == nil { - // shouldn't happen - slog.Warn("nil oneapi handle with driver count", "count", int(oHandles.oneapi.num_drivers)) - continue + // On windows we bundle the oneapi library one level above the runner dir + depPath = "" + if runtime.GOOS == "windows" && envconfig.RunnersDir() != "" { + depPath = filepath.Join(filepath.Dir(envconfig.RunnersDir()), "oneapi") } - devCount := C.oneapi_get_device_count(*oHandles.oneapi, C.int(d)) - for i := range devCount { - gpuInfo := OneapiGPUInfo{ - GpuInfo: GpuInfo{ - Library: "oneapi", - }, - driverIndex: int(d), - gpuIndex: int(i), + + for d := range oHandles.oneapi.num_drivers { + if oHandles.oneapi == nil { + // shouldn't happen + slog.Warn("nil oneapi handle with driver count", "count", int(oHandles.oneapi.num_drivers)) + continue + } + devCount := C.oneapi_get_device_count(*oHandles.oneapi, C.int(d)) + for i := range devCount { + gpuInfo := OneapiGPUInfo{ + GpuInfo: GpuInfo{ + Library: "oneapi", + }, + driverIndex: int(d), + gpuIndex: int(i), + } + // TODO - split bootstrapping from updating free memory + C.oneapi_check_vram(*oHandles.oneapi, C.int(d), i, &memInfo) + // TODO - convert this to MinimumMemory based on testing... + var totalFreeMem float64 = float64(memInfo.free) * 0.95 // work-around: leave some reserve vram for mkl lib used in ggml-sycl backend. + memInfo.free = C.uint64_t(totalFreeMem) + gpuInfo.TotalMemory = uint64(memInfo.total) + gpuInfo.FreeMemory = uint64(memInfo.free) + gpuInfo.ID = C.GoString(&memInfo.gpu_id[0]) + gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) + gpuInfo.DependencyPath = depPath + oneapiGPUs = append(oneapiGPUs, gpuInfo) } - // TODO - split bootstrapping from updating free memory - C.oneapi_check_vram(*oHandles.oneapi, C.int(d), i, &memInfo) - // TODO - convert this to MinimumMemory based on testing... - var totalFreeMem float64 = float64(memInfo.free) * 0.95 // work-around: leave some reserve vram for mkl lib used in ggml-sycl backend. - memInfo.free = C.uint64_t(totalFreeMem) - gpuInfo.TotalMemory = uint64(memInfo.total) - gpuInfo.FreeMemory = uint64(memInfo.free) - gpuInfo.ID = C.GoString(&memInfo.gpu_id[0]) - gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) - gpuInfo.DependencyPath = depPath - oneapiGPUs = append(oneapiGPUs, gpuInfo) } } } From 2fa1db434581bcfcb6fec1482904656e4b5f8313 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 9 Aug 2024 11:57:48 -0700 Subject: [PATCH 016/174] Don't hard fail on sparse setup error It seems this can fail in some casees, but proceed with the download anyway. --- server/download.go | 4 +--- server/sparse_common.go | 3 +-- server/sparse_windows.go | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/server/download.go b/server/download.go index 38d24a6b..5965b322 100644 --- a/server/download.go +++ b/server/download.go @@ -216,9 +216,7 @@ func (b *blobDownload) run(ctx context.Context, requestURL *url.URL, opts *regis return err } defer file.Close() - if err := setSparse(file); err != nil { - return err - } + setSparse(file) _ = file.Truncate(b.Total) diff --git a/server/sparse_common.go b/server/sparse_common.go index f25627fc..c88b2da0 100644 --- a/server/sparse_common.go +++ b/server/sparse_common.go @@ -4,6 +4,5 @@ package server import "os" -func setSparse(file *os.File) error { - return nil +func setSparse(*os.File) { } diff --git a/server/sparse_windows.go b/server/sparse_windows.go index cdad379e..f21cbbda 100644 --- a/server/sparse_windows.go +++ b/server/sparse_windows.go @@ -6,8 +6,9 @@ import ( "golang.org/x/sys/windows" ) -func setSparse(file *os.File) error { - return windows.DeviceIoControl( +func setSparse(file *os.File) { + // exFat (and other FS types) don't support sparse files, so ignore errors + windows.DeviceIoControl( //nolint:errcheck windows.Handle(file.Fd()), windows.FSCTL_SET_SPARSE, nil, 0, nil, 0, From d4e640746469ac586f12b400384c4ae7354d9280 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Fri, 9 Aug 2024 23:14:13 +0200 Subject: [PATCH 017/174] Restrict text files with explicit line feeds to *.go. This partially reverts b732beba6a919b852539bb344b05e25c6a7c3c90. It seems like explicitly setting all files to use line feeds was done due to issues with the go linter, hence it can be restricted to those files (https://github.com/ollama/ollama/pull/6235#issuecomment-2278745953). --- .gitattributes | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index baabd3c1..f1c8bcb4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,3 @@ llm/ext_server/* linguist-vendored -* text eol=lf -*.png binary -*.ico binary -*.icns binary +* text=auto +*.go text eol=lf From 023451ce471e7781bee65505011c48b9e5541811 Mon Sep 17 00:00:00 2001 From: CognitiveTech Date: Sat, 10 Aug 2024 21:43:08 -0400 Subject: [PATCH 018/174] add integration obook-summary (#6305) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7c606e1c..aae92e6c 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [tlm](https://github.com/yusufcanb/tlm) - [podman-ollama](https://github.com/ericcurtin/podman-ollama) - [gollama](https://github.com/sammcj/gollama) +- [Ollama eBook Summary](https://github.com/cognitivetech/ollama-ebook-summary/) ### Database From 25906d72d1482bc9dc2e4300a42c8db4823ee1a3 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Sun, 11 Aug 2024 11:30:20 -0700 Subject: [PATCH 019/174] llm: prevent loading too large models on windows (#5926) Don't allow loading models that would lead to memory exhaustion (across vram, system memory and disk paging). This check was already applied on Linux but should also be applied on Windows as well. --- llm/server.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llm/server.go b/llm/server.go index 41736068..0bd94f35 100644 --- a/llm/server.go +++ b/llm/server.go @@ -125,8 +125,9 @@ func NewLlamaServer(gpus gpu.GpuInfoList, model string, ggml *GGML, adapters, pr } } - // On linux, over-allocating CPU memory will almost always result in an error - if runtime.GOOS == "linux" { + // On linux and windows, over-allocating CPU memory will almost always result in an error + // Darwin has fully dynamic swap so has no direct concept of free swap space + if runtime.GOOS != "darwin" { systemMemoryRequired := estimate.TotalSize - estimate.VRAMSize available := systemFreeMemory + systemSwapFreeMemory if systemMemoryRequired > available { From 15c2d8fe149ba2b58aadbab615a6955f8821c7a9 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 11 Aug 2024 11:57:10 -0700 Subject: [PATCH 020/174] server: parallelize embeddings in API web handler instead of in subprocess runner (#6220) For simplicity, perform parallelization of embedding requests in the API handler instead of offloading this to the subprocess runner. This keeps the scheduling story simpler as it builds on existing parallel requests, similar to existing text completion functionality. --- llm/ext_server/server.cpp | 42 ++++++++------------------------------- llm/server.go | 32 +++++++++++++---------------- server/routes.go | 42 +++++++++++++++++++++++++-------------- server/sched_test.go | 8 ++++---- 4 files changed, 53 insertions(+), 71 deletions(-) diff --git a/llm/ext_server/server.cpp b/llm/ext_server/server.cpp index c65901c7..5717c17a 100644 --- a/llm/ext_server/server.cpp +++ b/llm/ext_server/server.cpp @@ -1223,9 +1223,7 @@ struct llama_server_context res.result_json = json { - {"id", res.id}, {"embedding", std::vector(embd, embd + n_embd)}, - {"timings", slot.get_formated_timings()}, }; } } @@ -3194,41 +3192,17 @@ int main(int argc, char **argv) { prompt = ""; } - if (prompt.size() == 1) { - prompt = prompt[0]; - } - // create and queue the task - json responses; - { - const int id_task = llama.queue_tasks.get_new_id(); - llama.queue_results.add_waiting_task_id(id_task); - llama.request_completion(id_task, {{"prompt", prompt}}, true, -1); + const int task_id = llama.queue_tasks.get_new_id(); + llama.queue_results.add_waiting_task_id(task_id); + llama.request_completion(task_id, {{"prompt", prompt}}, true, -1); - // get the result - task_result result = llama.queue_results.recv(id_task); - llama.queue_results.remove_waiting_task_id(id_task); - if (result.error) { - return res.set_content(result.result_json.dump(), "application/json; charset=utf-8"); - } + // get the result + task_result result = llama.queue_results.recv(task_id); + llama.queue_results.remove_waiting_task_id(task_id); - responses = result.result_json.value("results", std::vector{result.result_json}); - std::sort(responses.begin(), responses.end(), [](const json& a, const json& b) { - return a["id"] < b["id"]; - }); - - json embeddings = json::array(); - - int prompt_n = 0; - for (auto & elem : responses) { - embeddings.push_back(elem.at("embedding")); - prompt_n += elem.at("timings").at("prompt_n").get(); - } - - // send the result - json embedding_res = json{{"embedding", embeddings}, {"prompt_n", prompt_n}}; - return res.set_content(embedding_res.dump(), "application/json; charset=utf-8"); - } + // send the result + return res.set_content(result.result_json.dump(), "application/json; charset=utf-8"); }); // GG: if I put the main loop inside a thread, it crashes on the first request when build in Debug!? diff --git a/llm/server.go b/llm/server.go index 0bd94f35..d2b8db9b 100644 --- a/llm/server.go +++ b/llm/server.go @@ -33,7 +33,7 @@ type LlamaServer interface { Ping(ctx context.Context) error WaitUntilRunning(ctx context.Context) error Completion(ctx context.Context, req CompletionRequest, fn func(CompletionResponse)) error - Embed(ctx context.Context, input []string) (*EmbedResponse, error) + Embedding(ctx context.Context, input string) ([]float32, error) Tokenize(ctx context.Context, content string) ([]int, error) Detokenize(ctx context.Context, tokens []int) (string, error) Close() error @@ -883,24 +883,20 @@ func (s *llmServer) Completion(ctx context.Context, req CompletionRequest, fn fu return nil } -type EmbedRequest struct { - Content []string `json:"content"` +type EmbeddingRequest struct { + Content string `json:"content"` } -type EmbedResponse struct { - Embedding [][]float32 `json:"embedding"` - PromptEvalCount int `json:"prompt_n"` +type EmbeddingResponse struct { + Embedding []float32 `json:"embedding"` } -func (s *llmServer) Embed(ctx context.Context, input []string) (*EmbedResponse, error) { - // each input will use a slot, so we need to acquire the semaphore for - // the number of inputs up to numParallel - slots := int64(min(len(input), s.numParallel)) - if err := s.sem.Acquire(ctx, slots); err != nil { +func (s *llmServer) Embedding(ctx context.Context, input string) ([]float32, error) { + if err := s.sem.Acquire(ctx, 1); err != nil { slog.Error("Failed to acquire semaphore", "error", err) return nil, err } - defer s.sem.Release(slots) + defer s.sem.Release(1) // Make sure the server is ready status, err := s.getServerStatusRetry(ctx) @@ -910,18 +906,18 @@ func (s *llmServer) Embed(ctx context.Context, input []string) (*EmbedResponse, return nil, fmt.Errorf("unexpected server status: %s", status.ToString()) } - data, err := json.Marshal(EmbedRequest{Content: input}) + data, err := json.Marshal(EmbeddingRequest{Content: input}) if err != nil { return nil, fmt.Errorf("error marshaling embed data: %w", err) } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("http://127.0.0.1:%d/embedding", s.port), bytes.NewBuffer(data)) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("http://127.0.0.1:%d/embedding", s.port), bytes.NewBuffer(data)) if err != nil { return nil, fmt.Errorf("error creating embed request: %w", err) } - req.Header.Set("Content-Type", "application/json") + r.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := http.DefaultClient.Do(r) if err != nil { return nil, fmt.Errorf("do embedding request: %w", err) } @@ -937,12 +933,12 @@ func (s *llmServer) Embed(ctx context.Context, input []string) (*EmbedResponse, return nil, fmt.Errorf("%s", body) } - var e EmbedResponse + var e EmbeddingResponse if err := json.Unmarshal(body, &e); err != nil { return nil, fmt.Errorf("unmarshal tokenize response: %w", err) } - return &e, nil + return e.Embedding, nil } type TokenizeRequest struct { diff --git a/server/routes.go b/server/routes.go index e55eaa9d..e5a31002 100644 --- a/server/routes.go +++ b/server/routes.go @@ -23,6 +23,7 @@ import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" "github.com/ollama/ollama/api" "github.com/ollama/ollama/envconfig" @@ -346,6 +347,7 @@ func (s *Server) EmbedHandler(c *gin.Context) { return } + var count int for i, s := range input { tokens, err := r.Tokenize(c.Request.Context(), s) if err != nil { @@ -368,25 +370,36 @@ func (s *Server) EmbedHandler(c *gin.Context) { } } + count += len(tokens) + input[i] = s } - embeddings, err := r.Embed(c.Request.Context(), input) - if err != nil { - slog.Error("embedding generation failed", "error", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate embedding"}) - return + + var g errgroup.Group + embeddings := make([][]float32, len(input)) + for i, text := range input { + g.Go(func() error { + embedding, err := r.Embedding(c.Request.Context(), text) + if err != nil { + return err + } + embeddings[i] = normalize(embedding) + return nil + }) } - for i, e := range embeddings.Embedding { - embeddings.Embedding[i] = normalize(e) + if err := g.Wait(); err != nil { + slog.Error("embedding generation failed", "error", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Errorf("failed to generate embeddings: %v", err)}) + return } resp := api.EmbedResponse{ Model: req.Model, - Embeddings: embeddings.Embedding, + Embeddings: embeddings, TotalDuration: time.Since(checkpointStart), LoadDuration: checkpointLoaded.Sub(checkpointStart), - PromptEvalCount: embeddings.PromptEvalCount, + PromptEvalCount: count, } c.JSON(http.StatusOK, resp) } @@ -430,21 +443,20 @@ func (s *Server) EmbeddingsHandler(c *gin.Context) { return } - embeddings, err := r.Embed(c.Request.Context(), []string{req.Prompt}) + embedding, err := r.Embedding(c.Request.Context(), req.Prompt) if err != nil { slog.Info(fmt.Sprintf("embedding generation failed: %v", err)) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate embedding"}) return } - embedding := make([]float64, len(embeddings.Embedding[0])) - - for i, v := range embeddings.Embedding[0] { - embedding[i] = float64(v) + var e []float64 + for _, v := range embedding { + e = append(e, float64(v)) } resp := api.EmbeddingResponse{ - Embedding: embedding, + Embedding: e, } c.JSON(http.StatusOK, resp) } diff --git a/server/sched_test.go b/server/sched_test.go index c8717430..713b9259 100644 --- a/server/sched_test.go +++ b/server/sched_test.go @@ -708,8 +708,8 @@ type mockLlm struct { pingResp error waitResp error completionResp error - embedResp *llm.EmbedResponse - embedRespErr error + embeddingResp []float32 + embeddingRespErr error tokenizeResp []int tokenizeRespErr error detokenizeResp string @@ -727,8 +727,8 @@ func (s *mockLlm) Completion(ctx context.Context, req llm.CompletionRequest, fn return s.completionResp } -func (s *mockLlm) Embed(ctx context.Context, input []string) (*llm.EmbedResponse, error) { - return s.embedResp, s.embedRespErr +func (s *mockLlm) Embedding(ctx context.Context, input string) ([]float32, error) { + return s.embeddingResp, s.embeddingRespErr } func (s *mockLlm) Tokenize(ctx context.Context, content string) ([]int, error) { From 8aac22438ef34192ff804dbeb1b5e9a7e180eb7c Mon Sep 17 00:00:00 2001 From: Josh <76125168+joshyan1@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:28:55 -0700 Subject: [PATCH 021/174] server: speed up single gguf creates (#5898) --- server/model.go | 17 +++++++-- server/model_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/server/model.go b/server/model.go index ad6e4e55..8d7ed7e6 100644 --- a/server/model.go +++ b/server/model.go @@ -176,9 +176,20 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap mediatype = "application/vnd.ollama.image.projector" } - layer, err := NewLayer(io.NewSectionReader(file, offset, n), mediatype) - if err != nil { - return nil, err + var layer *Layer + if digest != "" && n == stat.Size() && offset == 0 { + layer, err = NewLayerFromLayer(digest, mediatype, file.Name()) + if err != nil { + slog.Debug("could not create new layer from layer", "error", err) + } + } + + // Fallback to creating layer from file copy (either NewLayerFromLayer failed, or digest empty/n != stat.Size()) + if layer == nil { + layer, err = NewLayer(io.NewSectionReader(file, offset, n), mediatype) + if err != nil { + return nil, err + } } layers = append(layers, &layerGGML{layer, ggml}) diff --git a/server/model_test.go b/server/model_test.go index aa214d3d..63fc408d 100644 --- a/server/model_test.go +++ b/server/model_test.go @@ -2,8 +2,10 @@ package server import ( "bytes" + "context" "encoding/json" "fmt" + "io" "os" "path/filepath" "testing" @@ -11,6 +13,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/ollama/ollama/api" + "github.com/ollama/ollama/llm" "github.com/ollama/ollama/template" ) @@ -133,3 +136,82 @@ The temperature in San Francisco, CA is 70°F and in Toronto, Canada is 20°C.`, }) } } + +func TestParseFromFileFromLayer(t *testing.T) { + tempModels := t.TempDir() + + file, err := os.CreateTemp(tempModels, "") + if err != nil { + t.Fatalf("failed to open file: %v", err) + } + defer file.Close() + if err := llm.WriteGGUF(file, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { + t.Fatalf("failed to write gguf: %v", err) + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers, err := parseFromFile(context.Background(), file, "", func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + + if len(layers) != 1 { + t.Fatalf("got %d != want 1", len(layers)) + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers2, err := parseFromFile(context.Background(), file, layers[0].Digest, func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + if len(layers2) != 1 { + t.Fatalf("got %d != want 1", len(layers2)) + } + + if layers[0].Digest != layers2[0].Digest { + t.Fatalf("got %s != want %s", layers[0].Digest, layers2[0].Digest) + } + + if layers[0].Size != layers2[0].Size { + t.Fatalf("got %d != want %d", layers[0].Size, layers2[0].Size) + } + + if layers[0].MediaType != layers2[0].MediaType { + t.Fatalf("got %v != want %v", layers[0].MediaType, layers2[0].MediaType) + } +} + +func TestParseLayerFromCopy(t *testing.T) { + tempModels := t.TempDir() + + file2, err := os.CreateTemp(tempModels, "") + if err != nil { + t.Fatalf("failed to open file: %v", err) + } + defer file2.Close() + + for range 5 { + if err := llm.WriteGGUF(file2, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { + t.Fatalf("failed to write gguf: %v", err) + } + } + + if _, err := file2.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers, err := parseFromFile(context.Background(), file2, "", func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + + if len(layers) != 5 { + t.Fatalf("got %d != want 5", len(layers)) + } +} From 1dc3ef3aa9d451a63fcb6ea2e1b6ea5289a1a325 Mon Sep 17 00:00:00 2001 From: Josh <76125168+joshyan1@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:57:51 -0700 Subject: [PATCH 022/174] Revert "server: speed up single gguf creates (#5898)" (#6323) This reverts commit 8aac22438ef34192ff804dbeb1b5e9a7e180eb7c. --- server/model.go | 17 ++------- server/model_test.go | 82 -------------------------------------------- 2 files changed, 3 insertions(+), 96 deletions(-) diff --git a/server/model.go b/server/model.go index 8d7ed7e6..ad6e4e55 100644 --- a/server/model.go +++ b/server/model.go @@ -176,20 +176,9 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap mediatype = "application/vnd.ollama.image.projector" } - var layer *Layer - if digest != "" && n == stat.Size() && offset == 0 { - layer, err = NewLayerFromLayer(digest, mediatype, file.Name()) - if err != nil { - slog.Debug("could not create new layer from layer", "error", err) - } - } - - // Fallback to creating layer from file copy (either NewLayerFromLayer failed, or digest empty/n != stat.Size()) - if layer == nil { - layer, err = NewLayer(io.NewSectionReader(file, offset, n), mediatype) - if err != nil { - return nil, err - } + layer, err := NewLayer(io.NewSectionReader(file, offset, n), mediatype) + if err != nil { + return nil, err } layers = append(layers, &layerGGML{layer, ggml}) diff --git a/server/model_test.go b/server/model_test.go index 63fc408d..aa214d3d 100644 --- a/server/model_test.go +++ b/server/model_test.go @@ -2,10 +2,8 @@ package server import ( "bytes" - "context" "encoding/json" "fmt" - "io" "os" "path/filepath" "testing" @@ -13,7 +11,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/ollama/ollama/api" - "github.com/ollama/ollama/llm" "github.com/ollama/ollama/template" ) @@ -136,82 +133,3 @@ The temperature in San Francisco, CA is 70°F and in Toronto, Canada is 20°C.`, }) } } - -func TestParseFromFileFromLayer(t *testing.T) { - tempModels := t.TempDir() - - file, err := os.CreateTemp(tempModels, "") - if err != nil { - t.Fatalf("failed to open file: %v", err) - } - defer file.Close() - if err := llm.WriteGGUF(file, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { - t.Fatalf("failed to write gguf: %v", err) - } - - if _, err := file.Seek(0, io.SeekStart); err != nil { - t.Fatalf("failed to seek to start: %v", err) - } - - layers, err := parseFromFile(context.Background(), file, "", func(api.ProgressResponse) {}) - if err != nil { - t.Fatalf("failed to parse from file: %v", err) - } - - if len(layers) != 1 { - t.Fatalf("got %d != want 1", len(layers)) - } - - if _, err := file.Seek(0, io.SeekStart); err != nil { - t.Fatalf("failed to seek to start: %v", err) - } - - layers2, err := parseFromFile(context.Background(), file, layers[0].Digest, func(api.ProgressResponse) {}) - if err != nil { - t.Fatalf("failed to parse from file: %v", err) - } - if len(layers2) != 1 { - t.Fatalf("got %d != want 1", len(layers2)) - } - - if layers[0].Digest != layers2[0].Digest { - t.Fatalf("got %s != want %s", layers[0].Digest, layers2[0].Digest) - } - - if layers[0].Size != layers2[0].Size { - t.Fatalf("got %d != want %d", layers[0].Size, layers2[0].Size) - } - - if layers[0].MediaType != layers2[0].MediaType { - t.Fatalf("got %v != want %v", layers[0].MediaType, layers2[0].MediaType) - } -} - -func TestParseLayerFromCopy(t *testing.T) { - tempModels := t.TempDir() - - file2, err := os.CreateTemp(tempModels, "") - if err != nil { - t.Fatalf("failed to open file: %v", err) - } - defer file2.Close() - - for range 5 { - if err := llm.WriteGGUF(file2, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { - t.Fatalf("failed to write gguf: %v", err) - } - } - - if _, err := file2.Seek(0, io.SeekStart); err != nil { - t.Fatalf("failed to seek to start: %v", err) - } - - layers, err := parseFromFile(context.Background(), file2, "", func(api.ProgressResponse) {}) - if err != nil { - t.Fatalf("failed to parse from file: %v", err) - } - - if len(layers) != 5 { - t.Fatalf("got %d != want 5", len(layers)) - } -} From 01d544d373d0f7782a9da2a830e0e7fa6926a584 Mon Sep 17 00:00:00 2001 From: royjhan <65097070+royjhan@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:33:34 -0400 Subject: [PATCH 023/174] OpenAI: Simplify input output in testing (#5858) * simplify input output * direct comp * in line image * rm error pointer type * update response testing * lint --- openai/openai_test.go | 668 ++++++++++++++++++++++-------------------- 1 file changed, 344 insertions(+), 324 deletions(-) diff --git a/openai/openai_test.go b/openai/openai_test.go index e08a96c9..c7e9f384 100644 --- a/openai/openai_test.go +++ b/openai/openai_test.go @@ -7,27 +7,22 @@ import ( "io" "net/http" "net/http/httptest" + "reflect" "strings" "testing" "time" "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" "github.com/ollama/ollama/api" ) const ( - prefix = `data:image/jpeg;base64,` - image = `iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=` - imageURL = prefix + image + prefix = `data:image/jpeg;base64,` + image = `iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=` ) -func prepareRequest(req *http.Request, body any) { - bodyBytes, _ := json.Marshal(body) - req.Body = io.NopCloser(bytes.NewReader(bodyBytes)) - req.Header.Set("Content-Type", "application/json") -} +var False = false func captureRequestMiddleware(capturedRequest any) gin.HandlerFunc { return func(c *gin.Context) { @@ -43,134 +38,136 @@ func captureRequestMiddleware(capturedRequest any) gin.HandlerFunc { func TestChatMiddleware(t *testing.T) { type testCase struct { - Name string - Setup func(t *testing.T, req *http.Request) - Expected func(t *testing.T, req *api.ChatRequest, resp *httptest.ResponseRecorder) + name string + body string + req api.ChatRequest + err ErrorResponse } var capturedRequest *api.ChatRequest testCases := []testCase{ { - Name: "chat handler", - Setup: func(t *testing.T, req *http.Request) { - body := ChatCompletionRequest{ - Model: "test-model", - Messages: []Message{{Role: "user", Content: "Hello"}}, - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.ChatRequest, resp *httptest.ResponseRecorder) { - if resp.Code != http.StatusOK { - t.Fatalf("expected 200, got %d", resp.Code) - } - - if req.Messages[0].Role != "user" { - t.Fatalf("expected 'user', got %s", req.Messages[0].Role) - } - - if req.Messages[0].Content != "Hello" { - t.Fatalf("expected 'Hello', got %s", req.Messages[0].Content) - } + name: "chat handler", + body: `{ + "model": "test-model", + "messages": [ + {"role": "user", "content": "Hello"} + ] + }`, + req: api.ChatRequest{ + Model: "test-model", + Messages: []api.Message{ + { + Role: "user", + Content: "Hello", + }, + }, + Options: map[string]any{ + "temperature": 1.0, + "top_p": 1.0, + }, + Stream: &False, }, }, { - Name: "chat handler with image content", - Setup: func(t *testing.T, req *http.Request) { - body := ChatCompletionRequest{ - Model: "test-model", - Messages: []Message{ - { - Role: "user", Content: []map[string]any{ - {"type": "text", "text": "Hello"}, - {"type": "image_url", "image_url": map[string]string{"url": imageURL}}, + name: "chat handler with image content", + body: `{ + "model": "test-model", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello" + }, + { + "type": "image_url", + "image_url": { + "url": "` + prefix + image + `" + } + } + ] + } + ] + }`, + req: api.ChatRequest{ + Model: "test-model", + Messages: []api.Message{ + { + Role: "user", + Content: "Hello", + }, + { + Role: "user", + Images: []api.ImageData{ + func() []byte { + img, _ := base64.StdEncoding.DecodeString(image) + return img + }(), + }, + }, + }, + Options: map[string]any{ + "temperature": 1.0, + "top_p": 1.0, + }, + Stream: &False, + }, + }, + { + name: "chat handler with tools", + body: `{ + "model": "test-model", + "messages": [ + {"role": "user", "content": "What's the weather like in Paris Today?"}, + {"role": "assistant", "tool_calls": [{"id": "id", "type": "function", "function": {"name": "get_current_weather", "arguments": "{\"location\": \"Paris, France\", \"format\": \"celsius\"}"}}]} + ] + }`, + req: api.ChatRequest{ + Model: "test-model", + Messages: []api.Message{ + { + Role: "user", + Content: "What's the weather like in Paris Today?", + }, + { + Role: "assistant", + ToolCalls: []api.ToolCall{ + { + Function: api.ToolCallFunction{ + Name: "get_current_weather", + Arguments: map[string]interface{}{ + "location": "Paris, France", + "format": "celsius", + }, + }, }, }, }, - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.ChatRequest, resp *httptest.ResponseRecorder) { - if resp.Code != http.StatusOK { - t.Fatalf("expected 200, got %d", resp.Code) - } - - if req.Messages[0].Role != "user" { - t.Fatalf("expected 'user', got %s", req.Messages[0].Role) - } - - if req.Messages[0].Content != "Hello" { - t.Fatalf("expected 'Hello', got %s", req.Messages[0].Content) - } - - img, _ := base64.StdEncoding.DecodeString(imageURL[len(prefix):]) - - if req.Messages[1].Role != "user" { - t.Fatalf("expected 'user', got %s", req.Messages[1].Role) - } - - if !bytes.Equal(req.Messages[1].Images[0], img) { - t.Fatalf("expected image encoding, got %s", req.Messages[1].Images[0]) - } + }, + Options: map[string]any{ + "temperature": 1.0, + "top_p": 1.0, + }, + Stream: &False, }, }, + { - Name: "chat handler with tools", - Setup: func(t *testing.T, req *http.Request) { - body := ChatCompletionRequest{ - Model: "test-model", - Messages: []Message{ - {Role: "user", Content: "What's the weather like in Paris Today?"}, - {Role: "assistant", ToolCalls: []ToolCall{{ - ID: "id", - Type: "function", - Function: struct { - Name string `json:"name"` - Arguments string `json:"arguments"` - }{ - Name: "get_current_weather", - Arguments: "{\"location\": \"Paris, France\", \"format\": \"celsius\"}", - }, - }}}, - }, - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.ChatRequest, resp *httptest.ResponseRecorder) { - if resp.Code != 200 { - t.Fatalf("expected 200, got %d", resp.Code) - } - - if req.Messages[0].Content != "What's the weather like in Paris Today?" { - t.Fatalf("expected What's the weather like in Paris Today?, got %s", req.Messages[0].Content) - } - - if req.Messages[1].ToolCalls[0].Function.Arguments["location"] != "Paris, France" { - t.Fatalf("expected 'Paris, France', got %v", req.Messages[1].ToolCalls[0].Function.Arguments["location"]) - } - - if req.Messages[1].ToolCalls[0].Function.Arguments["format"] != "celsius" { - t.Fatalf("expected celsius, got %v", req.Messages[1].ToolCalls[0].Function.Arguments["format"]) - } - }, - }, - { - Name: "chat handler error forwarding", - Setup: func(t *testing.T, req *http.Request) { - body := ChatCompletionRequest{ - Model: "test-model", - Messages: []Message{{Role: "user", Content: 2}}, - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.ChatRequest, resp *httptest.ResponseRecorder) { - if resp.Code != http.StatusBadRequest { - t.Fatalf("expected 400, got %d", resp.Code) - } - - if !strings.Contains(resp.Body.String(), "invalid message content type") { - t.Fatalf("error was not forwarded") - } + name: "chat handler error forwarding", + body: `{ + "model": "test-model", + "messages": [ + {"role": "user", "content": 2} + ] + }`, + err: ErrorResponse{ + Error: Error{ + Message: "invalid message content type: float64", + Type: "invalid_request_error", + }, }, }, } @@ -185,16 +182,26 @@ func TestChatMiddleware(t *testing.T) { router.Handle(http.MethodPost, "/api/chat", endpoint) for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - req, _ := http.NewRequest(http.MethodPost, "/api/chat", nil) - - tc.Setup(t, req) + t.Run(tc.name, func(t *testing.T) { + req, _ := http.NewRequest(http.MethodPost, "/api/chat", strings.NewReader(tc.body)) + req.Header.Set("Content-Type", "application/json") resp := httptest.NewRecorder() router.ServeHTTP(resp, req) - tc.Expected(t, capturedRequest, resp) + var errResp ErrorResponse + if resp.Code != http.StatusOK { + if err := json.Unmarshal(resp.Body.Bytes(), &errResp); err != nil { + t.Fatal(err) + } + } + if capturedRequest != nil && !reflect.DeepEqual(tc.req, *capturedRequest) { + t.Fatal("requests did not match") + } + if !reflect.DeepEqual(tc.err, errResp) { + t.Fatal("errors did not match") + } capturedRequest = nil }) } @@ -202,71 +209,52 @@ func TestChatMiddleware(t *testing.T) { func TestCompletionsMiddleware(t *testing.T) { type testCase struct { - Name string - Setup func(t *testing.T, req *http.Request) - Expected func(t *testing.T, req *api.GenerateRequest, resp *httptest.ResponseRecorder) + name string + body string + req api.GenerateRequest + err ErrorResponse } var capturedRequest *api.GenerateRequest testCases := []testCase{ { - Name: "completions handler", - Setup: func(t *testing.T, req *http.Request) { - temp := float32(0.8) - body := CompletionRequest{ - Model: "test-model", - Prompt: "Hello", - Temperature: &temp, - Stop: []string{"\n", "stop"}, - Suffix: "suffix", - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.GenerateRequest, resp *httptest.ResponseRecorder) { - if req.Prompt != "Hello" { - t.Fatalf("expected 'Hello', got %s", req.Prompt) - } - - if req.Options["temperature"] != 1.6 { - t.Fatalf("expected 1.6, got %f", req.Options["temperature"]) - } - - stopTokens, ok := req.Options["stop"].([]any) - - if !ok { - t.Fatalf("expected stop tokens to be a list") - } - - if stopTokens[0] != "\n" || stopTokens[1] != "stop" { - t.Fatalf("expected ['\\n', 'stop'], got %v", stopTokens) - } - - if req.Suffix != "suffix" { - t.Fatalf("expected 'suffix', got %s", req.Suffix) - } + name: "completions handler", + body: `{ + "model": "test-model", + "prompt": "Hello", + "temperature": 0.8, + "stop": ["\n", "stop"], + "suffix": "suffix" + }`, + req: api.GenerateRequest{ + Model: "test-model", + Prompt: "Hello", + Options: map[string]any{ + "frequency_penalty": 0.0, + "presence_penalty": 0.0, + "temperature": 1.6, + "top_p": 1.0, + "stop": []any{"\n", "stop"}, + }, + Suffix: "suffix", + Stream: &False, }, }, { - Name: "completions handler error forwarding", - Setup: func(t *testing.T, req *http.Request) { - body := CompletionRequest{ - Model: "test-model", - Prompt: "Hello", - Temperature: nil, - Stop: []int{1, 2}, - Suffix: "suffix", - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.GenerateRequest, resp *httptest.ResponseRecorder) { - if resp.Code != http.StatusBadRequest { - t.Fatalf("expected 400, got %d", resp.Code) - } - - if !strings.Contains(resp.Body.String(), "invalid type for 'stop' field") { - t.Fatalf("error was not forwarded") - } + name: "completions handler error forwarding", + body: `{ + "model": "test-model", + "prompt": "Hello", + "temperature": null, + "stop": [1, 2], + "suffix": "suffix" + }`, + err: ErrorResponse{ + Error: Error{ + Message: "invalid type for 'stop' field: float64", + Type: "invalid_request_error", + }, }, }, } @@ -281,15 +269,27 @@ func TestCompletionsMiddleware(t *testing.T) { router.Handle(http.MethodPost, "/api/generate", endpoint) for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - req, _ := http.NewRequest(http.MethodPost, "/api/generate", nil) - - tc.Setup(t, req) + t.Run(tc.name, func(t *testing.T) { + req, _ := http.NewRequest(http.MethodPost, "/api/generate", strings.NewReader(tc.body)) + req.Header.Set("Content-Type", "application/json") resp := httptest.NewRecorder() router.ServeHTTP(resp, req) - tc.Expected(t, capturedRequest, resp) + var errResp ErrorResponse + if resp.Code != http.StatusOK { + if err := json.Unmarshal(resp.Body.Bytes(), &errResp); err != nil { + t.Fatal(err) + } + } + + if capturedRequest != nil && !reflect.DeepEqual(tc.req, *capturedRequest) { + t.Fatal("requests did not match") + } + + if !reflect.DeepEqual(tc.err, errResp) { + t.Fatal("errors did not match") + } capturedRequest = nil }) @@ -298,78 +298,47 @@ func TestCompletionsMiddleware(t *testing.T) { func TestEmbeddingsMiddleware(t *testing.T) { type testCase struct { - Name string - Setup func(t *testing.T, req *http.Request) - Expected func(t *testing.T, req *api.EmbedRequest, resp *httptest.ResponseRecorder) + name string + body string + req api.EmbedRequest + err ErrorResponse } var capturedRequest *api.EmbedRequest testCases := []testCase{ { - Name: "embed handler single input", - Setup: func(t *testing.T, req *http.Request) { - body := EmbedRequest{ - Input: "Hello", - Model: "test-model", - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.EmbedRequest, resp *httptest.ResponseRecorder) { - if req.Input != "Hello" { - t.Fatalf("expected 'Hello', got %s", req.Input) - } - - if req.Model != "test-model" { - t.Fatalf("expected 'test-model', got %s", req.Model) - } + name: "embed handler single input", + body: `{ + "input": "Hello", + "model": "test-model" + }`, + req: api.EmbedRequest{ + Input: "Hello", + Model: "test-model", }, }, { - Name: "embed handler batch input", - Setup: func(t *testing.T, req *http.Request) { - body := EmbedRequest{ - Input: []string{"Hello", "World"}, - Model: "test-model", - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.EmbedRequest, resp *httptest.ResponseRecorder) { - input, ok := req.Input.([]any) - - if !ok { - t.Fatalf("expected input to be a list") - } - - if input[0].(string) != "Hello" { - t.Fatalf("expected 'Hello', got %s", input[0]) - } - - if input[1].(string) != "World" { - t.Fatalf("expected 'World', got %s", input[1]) - } - - if req.Model != "test-model" { - t.Fatalf("expected 'test-model', got %s", req.Model) - } + name: "embed handler batch input", + body: `{ + "input": ["Hello", "World"], + "model": "test-model" + }`, + req: api.EmbedRequest{ + Input: []any{"Hello", "World"}, + Model: "test-model", }, }, { - Name: "embed handler error forwarding", - Setup: func(t *testing.T, req *http.Request) { - body := EmbedRequest{ - Model: "test-model", - } - prepareRequest(req, body) - }, - Expected: func(t *testing.T, req *api.EmbedRequest, resp *httptest.ResponseRecorder) { - if resp.Code != http.StatusBadRequest { - t.Fatalf("expected 400, got %d", resp.Code) - } - - if !strings.Contains(resp.Body.String(), "invalid input") { - t.Fatalf("error was not forwarded") - } + name: "embed handler error forwarding", + body: `{ + "model": "test-model" + }`, + err: ErrorResponse{ + Error: Error{ + Message: "invalid input", + Type: "invalid_request_error", + }, }, }, } @@ -384,116 +353,167 @@ func TestEmbeddingsMiddleware(t *testing.T) { router.Handle(http.MethodPost, "/api/embed", endpoint) for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - req, _ := http.NewRequest(http.MethodPost, "/api/embed", nil) - - tc.Setup(t, req) + t.Run(tc.name, func(t *testing.T) { + req, _ := http.NewRequest(http.MethodPost, "/api/embed", strings.NewReader(tc.body)) + req.Header.Set("Content-Type", "application/json") resp := httptest.NewRecorder() router.ServeHTTP(resp, req) - tc.Expected(t, capturedRequest, resp) + var errResp ErrorResponse + if resp.Code != http.StatusOK { + if err := json.Unmarshal(resp.Body.Bytes(), &errResp); err != nil { + t.Fatal(err) + } + } + + if capturedRequest != nil && !reflect.DeepEqual(tc.req, *capturedRequest) { + t.Fatal("requests did not match") + } + + if !reflect.DeepEqual(tc.err, errResp) { + t.Fatal("errors did not match") + } capturedRequest = nil }) } } -func TestMiddlewareResponses(t *testing.T) { +func TestListMiddleware(t *testing.T) { type testCase struct { - Name string - Method string - Path string - TestPath string - Handler func() gin.HandlerFunc - Endpoint func(c *gin.Context) - Setup func(t *testing.T, req *http.Request) - Expected func(t *testing.T, resp *httptest.ResponseRecorder) + name string + endpoint func(c *gin.Context) + resp string } testCases := []testCase{ { - Name: "list handler", - Method: http.MethodGet, - Path: "/api/tags", - TestPath: "/api/tags", - Handler: ListMiddleware, - Endpoint: func(c *gin.Context) { + name: "list handler", + endpoint: func(c *gin.Context) { c.JSON(http.StatusOK, api.ListResponse{ Models: []api.ListModelResponse{ { - Name: "Test Model", + Name: "test-model", + ModifiedAt: time.Unix(int64(1686935002), 0).UTC(), }, }, }) }, - Expected: func(t *testing.T, resp *httptest.ResponseRecorder) { - var listResp ListCompletion - if err := json.NewDecoder(resp.Body).Decode(&listResp); err != nil { - t.Fatal(err) - } - - if listResp.Object != "list" { - t.Fatalf("expected list, got %s", listResp.Object) - } - - if len(listResp.Data) != 1 { - t.Fatalf("expected 1, got %d", len(listResp.Data)) - } - - if listResp.Data[0].Id != "Test Model" { - t.Fatalf("expected Test Model, got %s", listResp.Data[0].Id) - } - }, + resp: `{ + "object": "list", + "data": [ + { + "id": "test-model", + "object": "model", + "created": 1686935002, + "owned_by": "library" + } + ] + }`, }, { - Name: "retrieve model", - Method: http.MethodGet, - Path: "/api/show/:model", - TestPath: "/api/show/test-model", - Handler: RetrieveMiddleware, - Endpoint: func(c *gin.Context) { - c.JSON(http.StatusOK, api.ShowResponse{ - ModifiedAt: time.Date(2024, 6, 17, 13, 45, 0, 0, time.UTC), - }) - }, - Expected: func(t *testing.T, resp *httptest.ResponseRecorder) { - var retrieveResp Model - if err := json.NewDecoder(resp.Body).Decode(&retrieveResp); err != nil { - t.Fatal(err) - } - - if retrieveResp.Object != "model" { - t.Fatalf("Expected object to be model, got %s", retrieveResp.Object) - } - - if retrieveResp.Id != "test-model" { - t.Fatalf("Expected id to be test-model, got %s", retrieveResp.Id) - } + name: "list handler empty output", + endpoint: func(c *gin.Context) { + c.JSON(http.StatusOK, api.ListResponse{}) }, + resp: `{ + "object": "list", + "data": null + }`, }, } gin.SetMode(gin.TestMode) - router := gin.New() for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - router = gin.New() - router.Use(tc.Handler()) - router.Handle(tc.Method, tc.Path, tc.Endpoint) - req, _ := http.NewRequest(tc.Method, tc.TestPath, nil) + router := gin.New() + router.Use(ListMiddleware()) + router.Handle(http.MethodGet, "/api/tags", tc.endpoint) + req, _ := http.NewRequest(http.MethodGet, "/api/tags", nil) - if tc.Setup != nil { - tc.Setup(t, req) - } + resp := httptest.NewRecorder() + router.ServeHTTP(resp, req) - resp := httptest.NewRecorder() - router.ServeHTTP(resp, req) + var expected, actual map[string]any + err := json.Unmarshal([]byte(tc.resp), &expected) + if err != nil { + t.Fatalf("failed to unmarshal expected response: %v", err) + } - assert.Equal(t, http.StatusOK, resp.Code) + err = json.Unmarshal(resp.Body.Bytes(), &actual) + if err != nil { + t.Fatalf("failed to unmarshal actual response: %v", err) + } - tc.Expected(t, resp) - }) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("responses did not match\nExpected: %+v\nActual: %+v", expected, actual) + } + } +} + +func TestRetrieveMiddleware(t *testing.T) { + type testCase struct { + name string + endpoint func(c *gin.Context) + resp string + } + + testCases := []testCase{ + { + name: "retrieve handler", + endpoint: func(c *gin.Context) { + c.JSON(http.StatusOK, api.ShowResponse{ + ModifiedAt: time.Unix(int64(1686935002), 0).UTC(), + }) + }, + resp: `{ + "id":"test-model", + "object":"model", + "created":1686935002, + "owned_by":"library"} + `, + }, + { + name: "retrieve handler error forwarding", + endpoint: func(c *gin.Context) { + c.JSON(http.StatusBadRequest, gin.H{"error": "model not found"}) + }, + resp: `{ + "error": { + "code": null, + "message": "model not found", + "param": null, + "type": "api_error" + } + }`, + }, + } + + gin.SetMode(gin.TestMode) + + for _, tc := range testCases { + router := gin.New() + router.Use(RetrieveMiddleware()) + router.Handle(http.MethodGet, "/api/show/:model", tc.endpoint) + req, _ := http.NewRequest(http.MethodGet, "/api/show/test-model", nil) + + resp := httptest.NewRecorder() + router.ServeHTTP(resp, req) + + var expected, actual map[string]any + err := json.Unmarshal([]byte(tc.resp), &expected) + if err != nil { + t.Fatalf("failed to unmarshal expected response: %v", err) + } + + err = json.Unmarshal(resp.Body.Bytes(), &actual) + if err != nil { + t.Fatalf("failed to unmarshal actual response: %v", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("responses did not match\nExpected: %+v\nActual: %+v", expected, actual) + } } } From 980dd15f81e9021c5165a1e516748d42cf134339 Mon Sep 17 00:00:00 2001 From: Josh <76125168+joshyan1@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:46:09 -0700 Subject: [PATCH 024/174] cmd: speed up gguf creates (#6324) --- server/model.go | 17 +++++++-- server/model_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/server/model.go b/server/model.go index ad6e4e55..b17bf0e3 100644 --- a/server/model.go +++ b/server/model.go @@ -176,9 +176,20 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap mediatype = "application/vnd.ollama.image.projector" } - layer, err := NewLayer(io.NewSectionReader(file, offset, n), mediatype) - if err != nil { - return nil, err + var layer Layer + if digest != "" && n == stat.Size() && offset == 0 { + layer, err = NewLayerFromLayer(digest, mediatype, file.Name()) + if err != nil { + slog.Debug("could not create new layer from layer", "error", err) + } + } + + // Fallback to creating layer from file copy (either NewLayerFromLayer failed, or digest empty/n != stat.Size()) + if layer.Digest == "" { + layer, err = NewLayer(io.NewSectionReader(file, offset, n), mediatype) + if err != nil { + return nil, err + } } layers = append(layers, &layerGGML{layer, ggml}) diff --git a/server/model_test.go b/server/model_test.go index aa214d3d..63fc408d 100644 --- a/server/model_test.go +++ b/server/model_test.go @@ -2,8 +2,10 @@ package server import ( "bytes" + "context" "encoding/json" "fmt" + "io" "os" "path/filepath" "testing" @@ -11,6 +13,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/ollama/ollama/api" + "github.com/ollama/ollama/llm" "github.com/ollama/ollama/template" ) @@ -133,3 +136,82 @@ The temperature in San Francisco, CA is 70°F and in Toronto, Canada is 20°C.`, }) } } + +func TestParseFromFileFromLayer(t *testing.T) { + tempModels := t.TempDir() + + file, err := os.CreateTemp(tempModels, "") + if err != nil { + t.Fatalf("failed to open file: %v", err) + } + defer file.Close() + if err := llm.WriteGGUF(file, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { + t.Fatalf("failed to write gguf: %v", err) + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers, err := parseFromFile(context.Background(), file, "", func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + + if len(layers) != 1 { + t.Fatalf("got %d != want 1", len(layers)) + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers2, err := parseFromFile(context.Background(), file, layers[0].Digest, func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + if len(layers2) != 1 { + t.Fatalf("got %d != want 1", len(layers2)) + } + + if layers[0].Digest != layers2[0].Digest { + t.Fatalf("got %s != want %s", layers[0].Digest, layers2[0].Digest) + } + + if layers[0].Size != layers2[0].Size { + t.Fatalf("got %d != want %d", layers[0].Size, layers2[0].Size) + } + + if layers[0].MediaType != layers2[0].MediaType { + t.Fatalf("got %v != want %v", layers[0].MediaType, layers2[0].MediaType) + } +} + +func TestParseLayerFromCopy(t *testing.T) { + tempModels := t.TempDir() + + file2, err := os.CreateTemp(tempModels, "") + if err != nil { + t.Fatalf("failed to open file: %v", err) + } + defer file2.Close() + + for range 5 { + if err := llm.WriteGGUF(file2, llm.KV{"general.architecture": "gemma"}, []llm.Tensor{}); err != nil { + t.Fatalf("failed to write gguf: %v", err) + } + } + + if _, err := file2.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to seek to start: %v", err) + } + + layers, err := parseFromFile(context.Background(), file2, "", func(api.ProgressResponse) {}) + if err != nil { + t.Fatalf("failed to parse from file: %v", err) + } + + if len(layers) != 5 { + t.Fatalf("got %d != want 5", len(layers)) + } +} From f7e3b9190f7e8f99bac8af432b9539e24cd3b57e Mon Sep 17 00:00:00 2001 From: Josh <76125168+joshyan1@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:46:32 -0700 Subject: [PATCH 025/174] cmd: spinner progress for transfer model data (#6100) --- cmd/cmd.go | 45 ++++++++++++++++++++++++++++++++++++++++++--- progress/spinner.go | 14 ++++++++++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index d47db65b..2356110e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -22,6 +22,7 @@ import ( "runtime" "slices" "strings" + "sync/atomic" "syscall" "time" @@ -78,6 +79,7 @@ func CreateHandler(cmd *cobra.Command, args []string) error { status := "transferring model data" spinner := progress.NewSpinner(status) p.Add(status, spinner) + defer p.Stop() for i := range modelfile.Commands { switch modelfile.Commands[i].Name { @@ -112,7 +114,7 @@ func CreateHandler(cmd *cobra.Command, args []string) error { path = tempfile } - digest, err := createBlob(cmd, client, path) + digest, err := createBlob(cmd, client, path, spinner) if err != nil { return err } @@ -263,13 +265,20 @@ func tempZipFiles(path string) (string, error) { return tempfile.Name(), nil } -func createBlob(cmd *cobra.Command, client *api.Client, path string) (string, error) { +func createBlob(cmd *cobra.Command, client *api.Client, path string, spinner *progress.Spinner) (string, error) { bin, err := os.Open(path) if err != nil { return "", err } defer bin.Close() + // Get file info to retrieve the size + fileInfo, err := bin.Stat() + if err != nil { + return "", err + } + fileSize := fileInfo.Size() + hash := sha256.New() if _, err := io.Copy(hash, bin); err != nil { return "", err @@ -279,13 +288,43 @@ func createBlob(cmd *cobra.Command, client *api.Client, path string) (string, er return "", err } + var pw progressWriter + status := "transferring model data 0%" + spinner.SetMessage(status) + + done := make(chan struct{}) + defer close(done) + + go func() { + ticker := time.NewTicker(60 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + spinner.SetMessage(fmt.Sprintf("transferring model data %d%%", int(100*pw.n.Load()/fileSize))) + case <-done: + spinner.SetMessage("transferring model data 100%") + return + } + } + }() + digest := fmt.Sprintf("sha256:%x", hash.Sum(nil)) - if err = client.CreateBlob(cmd.Context(), digest, bin); err != nil { + if err = client.CreateBlob(cmd.Context(), digest, io.TeeReader(bin, &pw)); err != nil { return "", err } return digest, nil } +type progressWriter struct { + n atomic.Int64 +} + +func (w *progressWriter) Write(p []byte) (n int, err error) { + w.n.Add(int64(len(p))) + return len(p), nil +} + func RunHandler(cmd *cobra.Command, args []string) error { interactive := true diff --git a/progress/spinner.go b/progress/spinner.go index 02f3f9fb..e39a45ee 100644 --- a/progress/spinner.go +++ b/progress/spinner.go @@ -3,11 +3,12 @@ package progress import ( "fmt" "strings" + "sync/atomic" "time" ) type Spinner struct { - message string + message atomic.Value messageWidth int parts []string @@ -21,20 +22,25 @@ type Spinner struct { func NewSpinner(message string) *Spinner { s := &Spinner{ - message: message, parts: []string{ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", }, started: time.Now(), } + s.SetMessage(message) go s.start() return s } +func (s *Spinner) SetMessage(message string) { + s.message.Store(message) +} + func (s *Spinner) String() string { var sb strings.Builder - if len(s.message) > 0 { - message := strings.TrimSpace(s.message) + + if message, ok := s.message.Load().(string); ok && len(message) > 0 { + message := strings.TrimSpace(message) if s.messageWidth > 0 && len(message) > s.messageWidth { message = message[:s.messageWidth] } From 6ffb5cb017a1c81970ac637907a8ba6fd151e0e7 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Mon, 3 Jun 2024 15:53:58 -0700 Subject: [PATCH 026/174] add conversion for microsoft phi 3 mini/medium 4k, 128 --- convert/convert.go | 6 + convert/convert_llama.go | 4 - convert/convert_phi3.go | 125 ++++++++++ convert/convert_test.go | 2 + .../testdata/Phi-3-mini-128k-instruct.json | 225 ++++++++++++++++++ llm/ggml.go | 8 + llm/gguf.go | 15 +- 7 files changed, 373 insertions(+), 12 deletions(-) create mode 100644 convert/convert_phi3.go create mode 100644 convert/testdata/Phi-3-mini-128k-instruct.json diff --git a/convert/convert.go b/convert/convert.go index b9461e4f..24c19aa4 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -27,6 +27,10 @@ func (Parameters) KV(t *Tokenizer) llm.KV { "tokenizer.ggml.token_type": t.Vocabulary.Types, } + if len(t.Merges) > 0 { + kv["tokenizer.ggml.merges"] = t.Merges + } + if t.Template != "" { kv["tokenizer.chat_template"] = t.Template } @@ -89,6 +93,8 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { conv = &mixtral{} case "GemmaForCausalLM": conv = &gemma{} + case "Phi3ForCausalLM": + conv = &phi3{} default: return errors.New("unsupported architecture") } diff --git a/convert/convert_llama.go b/convert/convert_llama.go index 0383a85e..178b13f3 100644 --- a/convert/convert_llama.go +++ b/convert/convert_llama.go @@ -90,10 +90,6 @@ func (p *llama) KV(t *Tokenizer) llm.KV { kv["llama.attention.value_length"] = p.HeadDim } - if len(t.Merges) > 0 { - kv["tokenizer.ggml.merges"] = t.Merges - } - return kv } diff --git a/convert/convert_phi3.go b/convert/convert_phi3.go new file mode 100644 index 00000000..7aa3ed15 --- /dev/null +++ b/convert/convert_phi3.go @@ -0,0 +1,125 @@ +package convert + +import ( + "cmp" + "encoding/binary" + "io" + "math" + "strings" + "sync" + + "github.com/ollama/ollama/llm" +) + +type phi3 struct { + Parameters + NumHiddenLayers uint32 `json:"num_hidden_layers"` + NLayers uint32 `json:"n_layers"` + HiddenSize uint32 `json:"hidden_size"` + NEmbd uint32 `json:"n_embd"` + IntermediateSize uint32 `json:"intermediate_size"` + NumAttentionHeads uint32 `json:"num_attention_heads"` + NHead uint32 `json:"n_head"` + NumKeyValueHeads uint32 `json:"num_key_value_heads"` + NHeadKV uint32 `json:"n_head_kv"` + RopeTheta float32 `json:"rope_theta"` + RopeScaling struct { + Type string `json:"type"` + LongFactor ropeFactor `json:"long_factor"` + ShortFactor ropeFactor `json:"short_factor"` + } `json:"rope_scaling"` + RMSNormEPS float32 `json:"rms_norm_eps"` + NPositions uint32 `json:"n_positions"` + MaxPositionEmbeddings uint32 `json:"max_position_embeddings"` + OriginalMaxPositionEmbeddings uint32 `json:"original_max_position_embeddings"` + SlidingWindow uint32 `json:"sliding_window"` +} + +var _ Converter = (*phi3)(nil) + +func (p *phi3) KV(t *Tokenizer) llm.KV { + kv := p.Parameters.KV(t) + kv["general.architecture"] = "phi3" + kv["general.name"] = "phi3" + kv["phi3.context_length"] = p.MaxPositionEmbeddings + kv["phi3.embedding_length"] = cmp.Or(p.HiddenSize, p.NEmbd) + kv["phi3.feed_forward_length"] = p.IntermediateSize + kv["phi3.block_count"] = cmp.Or(p.NumHiddenLayers, p.NLayers) + kv["phi3.attention.head_count"] = cmp.Or(p.NumAttentionHeads, p.NHead) + kv["phi3.attention.head_count_kv"] = cmp.Or(p.NumKeyValueHeads, p.NHeadKV) + kv["phi3.attention.layer_norm_rms_epsilon"] = p.RMSNormEPS + kv["phi3.rope.dimension_count"] = p.HiddenSize / cmp.Or(p.NumAttentionHeads, p.NHead) + kv["phi3.rope.freq_base"] = p.RopeTheta + kv["phi3.rope.scaling.original_context_length"] = p.OriginalMaxPositionEmbeddings + kv["phi3.attention.sliding_window"] = p.SlidingWindow + + scale := float64(p.MaxPositionEmbeddings) / float64(p.OriginalMaxPositionEmbeddings) + + switch p.RopeScaling.Type { + case "": + // no scaling + case "su": + kv["phi3.rope.scaling.attn_factor"] = float32(max(math.Sqrt(1+math.Log(scale)/math.Log(float64(p.OriginalMaxPositionEmbeddings))), 1.0)) + case "yarn": + kv["phi3.rope.scaling.attn_factor"] = float32(max(0.1*math.Log(scale)+1.0, 1.0)) + default: + panic("unknown rope scaling type") + } + + return kv +} + +func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { + var addRopeFactors sync.Once + + out := make([]llm.Tensor, 0, len(ts)+2) + for _, t := range ts { + name := p.tensorName(t.Name()) + if strings.HasPrefix(name, "blk.0.") { + addRopeFactors.Do(func() { + out = append(out, llm.Tensor{ + Name: "rope_factors_long.weight", + Kind: 0, + Shape: []uint64{uint64(len(p.RopeScaling.LongFactor))}, + WriterTo: p.RopeScaling.LongFactor, + }, llm.Tensor{ + Name: "rope_factors_short.weight", + Kind: 0, + Shape: []uint64{uint64(len(p.RopeScaling.ShortFactor))}, + WriterTo: p.RopeScaling.ShortFactor, + }) + }) + } + + out = append(out, llm.Tensor{ + Name: name, + Kind: t.Kind(), + Shape: t.Shape(), + WriterTo: t, + }) + } + + return out +} + +func (p *phi3) tensorName(n string) string { + return strings.NewReplacer( + "lm_head", "output", + "model.embed_tokens", "token_embd", + "model.norm", "output_norm", + "model.layers", "blk", + "input_layernorm", "attn_norm", + "self_attn.qkv_proj", "attn_qkv", + "self_attn.o_proj", "attn_output", + "mlp.down_proj", "ffn_down", + "mlp.gate_up_proj", "ffn_up", + "post_attention_layernorm", "ffn_norm", + ).Replace(n) +} + +type ropeFactor []float32 + +func (r ropeFactor) WriteTo(w io.Writer) (int64, error) { + err := binary.Write(w, binary.LittleEndian, r) + return 0, err +} diff --git a/convert/convert_test.go b/convert/convert_test.go index 88f38494..cb2c585e 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -65,6 +65,8 @@ func TestConvertFull(t *testing.T) { "Mistral-7B-Instruct-v0.2", "Mixtral-8x7B-Instruct-v0.1", "gemma-2b-it", + // microsoft/Phi-3-mini-128-instruct@d548c233192db00165d842bf8edff054bb3212f8 + "Phi-3-mini-128k-instruct", } for i := range cases { diff --git a/convert/testdata/Phi-3-mini-128k-instruct.json b/convert/testdata/Phi-3-mini-128k-instruct.json new file mode 100644 index 00000000..19296f5a --- /dev/null +++ b/convert/testdata/Phi-3-mini-128k-instruct.json @@ -0,0 +1,225 @@ +{ + "general.architecture": "phi3", + "general.file_type": "1", + "general.quantization_version": "2", + "phi3.block_count": "32", + "phi3.context_length": "131072", + "phi3.embedding_length": "3072", + "phi3.feed_forward_length": "8192", + "phi3.rope.scaling.original_context_length": "4096", + "phi3.rope.dimension_count": "96", + "phi3.rope.freq_base": "10000", + "phi3.rope.scaling.attn_factor": "1.1902381", + "phi3.attention.head_count": "32", + "phi3.attention.head_count_kv": "32", + "phi3.attention.layer_norm_rms_epsilon": "1e-05", + "phi3.attention.sliding_window": "262144", + "tokenizer.ggml.model": "llama", + "tokenizer.ggml.pre": "default", + "tokenizer.ggml.add_bos_token": "false", + "tokenizer.ggml.add_eos_token": "false", + "tokenizer.ggml.bos_token_id": "1", + "tokenizer.ggml.eos_token_id": "32000", + "tokenizer.ggml.unknown_token_id": "0", + "tokenizer.ggml.padding_token_id": "32000", + "tokenizer.ggml.scores": "6e37bcde2adc7e350e87c496eddd7a2124329c1dc66c5bf3ad3997253e4f7a62", + "tokenizer.ggml.token_type": "b6ecf55ec64ee67d87750bdb8d757a2c58bf78377e9f4219f5689a6c4dea57ce", + "tokenizer.ggml.tokens": "d168da3ddd3eee820916945fcb9baf24dd3cde42f606cffa2d19e7c8a8743918", + "blk.0.attn_norm.weight": "216aeb2c9e0c271f899e1ef2a63cceeb8f41e97642e84fada54b1d3c1c11cf25", + "blk.0.attn_output.weight": "b597d56f7188ffc1fafc273fadc59d41738cffd677ae98c61a62c3285b3a3099", + "blk.0.attn_qkv.weight": "d28a6b44e13f59be5483e4be2bedb544e346168d720aca27f47d1a5a722be91e", + "blk.0.ffn_down.weight": "4a691370e5a61fcbbf540fbcbf4c0f1d15dec0364528c0e916d0744f6262b63b", + "blk.0.ffn_norm.weight": "0c00af2b4a3128bec64a0cbb1084b042fdbe13d9ad0d03bd577f9449dfead338", + "blk.0.ffn_up.weight": "b32b52f790c1c083bfb8a3126dc1111cfeeb28dc8c584a930a1e5334cb176bf4", + "blk.1.attn_norm.weight": "68748011503c6c029e8e69a84a8e5a89338f378769627b6dbf7f93d715c292e1", + "blk.1.attn_output.weight": "2267344add13b048ca59e4377c86dc512be8046a57156901fa32a20fa74e4ee0", + "blk.1.attn_qkv.weight": "9109d2e3d7a2eacfda5226587b8be124a3bf44b972da7ebb17aa15795897eacc", + "blk.1.ffn_down.weight": "d675df4df4dd039c0c339ad6445d39eddd2004db6bf35bed6314c7497245a633", + "blk.1.ffn_norm.weight": "3b5767ae977bc8baaa06b06efdbea193b6b3ba605ce76d77a76ce317e935500c", + "blk.1.ffn_up.weight": "80dfd6d9d234b00334c89b8e0a02f81899c2efd377321c34ba5ba51a5f61b5ff", + "blk.2.attn_norm.weight": "6a6743b057e5088f145bc179e92c9bfb41163e7295d7b81c62e23dd89d2b59c4", + "blk.2.attn_output.weight": "bc5491ea54e0db81462d7d9b7d25cbdda380c2db8de041bd1c4ab7b76a1d19c3", + "blk.2.attn_qkv.weight": "a61287a9852e2f5aca9c100b471d98398b2913a3497c743de3c70ec9ddd7087f", + "blk.2.ffn_down.weight": "4fddcc382c8dceeab027fe43d8d44e67edb5e8ce4b9a1b7f773c87770380ade1", + "blk.2.ffn_norm.weight": "07e05f82b3f63f711db3b684ca79aed25c0657917e66f88af47348a82065c227", + "blk.2.ffn_up.weight": "4835a682ef1826c12df01ae7663fc45f9c82bc8e64b665f13fb7da8e201ec0fb", + "blk.3.attn_norm.weight": "f22aba7c03999ba7136f39cda747a39715e498699dc1716cd97fc5dfc58d1b1c", + "blk.3.attn_output.weight": "53b579855366fd786c5126b2b30aac4d583ca7bda56833c4865f5cadb5c18c6d", + "blk.3.attn_qkv.weight": "bb56aba78158123140fcea59c69ac562ca208f6d3086819417cdad8c50f333ad", + "blk.3.ffn_down.weight": "97280897a7cd86db2830c004bccc5bc094f50e293baded0189159a2019145a6e", + "blk.3.ffn_norm.weight": "10a8c99f8b57a960e8e0a1133c4a26f9148403d1b9bff2eff114917de996f3b5", + "blk.3.ffn_up.weight": "7324046c915e75d621b2043597a245a428d8eea31869135e6257a861491d8dcc", + "blk.4.attn_norm.weight": "507d8e164de94646edbfe33def8e8fbf7c9a6ee3fbaedb5000f72d9f51ec5e36", + "blk.4.attn_output.weight": "bbb3429e6efa98c150e0fdbf48c16180cbf0d0cbc1b3c253c6c319d78f4593a2", + "blk.4.attn_qkv.weight": "b95ee5be0786d3901273d806c339fe6c20e6bfffd2a20672a9f56af80921e8ab", + "blk.4.ffn_down.weight": "806bbf91df92a5a22bd5aa1ffb7fc2869f7293ffc7704771c290ecc583b27975", + "blk.4.ffn_norm.weight": "cfc2930a81df7aee3a5e7f726a15c1182233e868bf0d9d37f6b6ae6d8c15c234", + "blk.4.ffn_up.weight": "c3390c69533de2c8424e8069323ccc5d0c4543111535da04cf2c7d26745576aa", + "blk.5.attn_norm.weight": "0d71c4fbcefabbd021569442853d2fe90668b19409ae2805a718a829ca60beab", + "blk.5.attn_output.weight": "10ebd93629112bf2df5c30dd0953a4a5e9020306768283181ed426934d47e14f", + "blk.5.attn_qkv.weight": "5cb05633369f12d4b00e0ff787736bd846856682115720ebc6cce05270c334f6", + "blk.5.ffn_down.weight": "e28bcc5094212eafc7476dbc5b7a520d25b79578cbf4229d698e2655956a80ad", + "blk.5.ffn_norm.weight": "b6f2c4cf9f34bb4d59989f96165c14a67dc1e266ad0a6d0fcc49f1add929e6ff", + "blk.5.ffn_up.weight": "0f9ef99423cc07ebedc0e9cfa95809f2d7108d910bb4ef97ebc0b0309c440750", + "blk.6.attn_norm.weight": "b3edcc47a42218234f7564d7470611b49401a41ae8cd42123f86557c69f5d7f2", + "blk.6.attn_output.weight": "eb9b7d257b388bb5b8fe0515e5c6873317239cb94cda236e4b6ada2a6c57c65c", + "blk.6.attn_qkv.weight": "eb968081f478c52f07bd9c2761741e982dba33cc4eeadeea3557d391b9ac2106", + "blk.6.ffn_down.weight": "1b8588bb7463206290322695577dcfced300895d6e6f4b26966c53a9ae2f0f84", + "blk.6.ffn_norm.weight": "1219c04b7770983c77814200eefe743f46d15328ea2b12711e44f8103eab08d3", + "blk.6.ffn_up.weight": "197ef287239fec47c55677f0fbb66eaf0644f775bc382de843971730721394f6", + "blk.7.attn_norm.weight": "b630ad08c80d564ed1c024384818e9fd3f22a36cd7a14aa96e7e2759a8285099", + "blk.7.attn_output.weight": "970255aa750828a47d6b9d399f9612b5bf25aefe7dadbcba41fc416d0d4067c1", + "blk.7.attn_qkv.weight": "ebb157c880293e6de8d629f263ba8853ed1dbdc02c311d43432bb8cfbb310739", + "blk.7.ffn_down.weight": "24bcd4db4cba844c89f878b81843c373dbbc0675e889d32c5b12e63384a7b670", + "blk.7.ffn_norm.weight": "b9c6f71001808ee873ce7db8056e4b53fb4cccec8b7f0f312899b575fae39d39", + "blk.7.ffn_up.weight": "979f1828d227455c26015a2a11afe9dd05f2bb97a8ba6b38c8dab3f50e627401", + "blk.8.attn_norm.weight": "4e8e347e3775010b7112ee630f2f4f2383be7ff64e6ca6154b9b22566552eaa6", + "blk.8.attn_output.weight": "65a44babf44a435a1829945211b3168f9ec78ac3cb7a049a733e93d11f0d6659", + "blk.8.attn_qkv.weight": "343ed07671da400b040812a4058482fa38284b5d9af9becfed07417fe26ce747", + "blk.8.ffn_down.weight": "7fb7e073e3c2c503c4e9d60efa0988fed7398d900cc003695fe3fffd3e188b82", + "blk.8.ffn_norm.weight": "b07c1f655d8593e3892a2cf73f8a0c19ce8e5cb613fafbe7cbd430da8ce4c57d", + "blk.8.ffn_up.weight": "8b26e14de54b3fdc2e2d3ea41720f9d9c236a93688c3b7fd7bf43f5fbb327c9b", + "blk.9.attn_norm.weight": "46394d408a8e316916177e6aa261de32e137a82d729c0b1800b072f0c38c39b6", + "blk.9.attn_output.weight": "d57f3d46107947a7073373a0b35d6ecf7759b5df15406f4a3590a60666af6b16", + "blk.9.attn_qkv.weight": "14bb8ace8c5453148f4b536e9f4279c813f31136716947256f5cca333448639c", + "blk.9.ffn_down.weight": "2b8d98e2b5ed68338f6e4de43bf7de0c4858cc69103cd5177725f7444eec7694", + "blk.9.ffn_norm.weight": "41a499dfd418cc4c6b8c12313f673f7e2cd4a3f9c4065eb6c4feb5eed02fb542", + "blk.9.ffn_up.weight": "143aab7533a64b17fbe201490a6f674bc7f0bd370c094500b2e100419073d1c2", + "blk.10.attn_norm.weight": "ebb670aafd36816a794347287269d8f1a5b19c1e3c0a1e38023bc19fdba9b073", + "blk.10.attn_output.weight": "b5d65bbc0ed5e49fdd9d754bc18163cd042a285024d0cf6f954c503bc8c877cb", + "blk.10.attn_qkv.weight": "f06b15bac88da798fa34a62b03eaac0dbe8b846020516603c387541f2d8dd672", + "blk.10.ffn_down.weight": "fb091fcd1b4de25d1bea94d1755e255cb02914a030d23e3a234e57b8d46bde6e", + "blk.10.ffn_norm.weight": "eb347bdf9c40414af87e13a8e72e40b31f004b50f7cb366f1a219ced60a61355", + "blk.10.ffn_up.weight": "ed2d52fc881a173f404fe8a1067862c9856d6c3e0d2e90a330a7aa394e3f84d1", + "blk.11.attn_norm.weight": "64e252603cf010a0e502ca39fdf8d0a196a79aec67c0d2bb9213fc0cb80c47d4", + "blk.11.attn_output.weight": "228e33e21c69f52efc74fdfc831bc9af271e44b2a29a3dced1d64e667ce36eb5", + "blk.11.attn_qkv.weight": "ab9ce6d4ef9e42ee0da3f20a7708a3bbc5e79e967b05fa86ba946a05e2eb63eb", + "blk.11.ffn_down.weight": "0ca133b7835c98dc77c25d64e4eb7873778bdb5e4d22d8b80f920f46865b43bd", + "blk.11.ffn_norm.weight": "02455741a0dfd161c79aa1ecc381901721f229fdcda5615622a629631fb61cfd", + "blk.11.ffn_up.weight": "9fecdcc099fbb8e23c6b1ea9294702a027f4a58d265543ec5e7be79b8f63b354", + "blk.12.attn_norm.weight": "783bb459911b1b3609a9b2bdfe272f1670add73b5471da738e07ac47e2e07dfd", + "blk.12.attn_output.weight": "1e1a914c9e48b857206ac5a1f7cead994bc1ea91d5d4fff8c834d73f2e38ef5d", + "blk.12.attn_qkv.weight": "5953e7185ccb87fb4dae8f9426ec86315d4c7794326e8ab59b3a95d4af2189f0", + "blk.12.ffn_down.weight": "a3eecf0f394f86e2cfb48a5940a5c50ca86d71883b2f79fcc642a935fabce0d4", + "blk.12.ffn_norm.weight": "0a4272e41373c23bd72f10d2d82930aa3a1480aac75832bfbf01cebf0b86b6a4", + "blk.12.ffn_up.weight": "06f42776de3a7ceac3025f26a7a8bd20e062233cce2bdaa2183470dc4b30b87d", + "blk.13.attn_norm.weight": "5915da60fb03e201fa649faba780e5fdf1c761c262b206e5415cf83181f65780", + "blk.13.attn_output.weight": "4dbf6eab074fa3835fd32bd631a8208e511037d5056d2fd3015735cca7674ef7", + "blk.13.attn_qkv.weight": "d3d8339a1c4782d9e73d77fdebe154d3c5b83ac40c9175b3e91a4977d08f876b", + "blk.13.ffn_down.weight": "de6772b46a55e1fd42b007637dfbf68b6598e5d5b61622da0935002e1e192d3a", + "blk.13.ffn_norm.weight": "5a640ea3b8c7be49c95a58a2327e10d8e8d9d142504bde5c8091613e5b961d7a", + "blk.13.ffn_up.weight": "f35e3545e4bd3531b2e843b5efd31dee0c13c807ee6386e65473ba67bbec30d0", + "blk.14.attn_norm.weight": "9b34986450b7c98b4927e81e61a816f9e84b1addc7c14926402100037aad6678", + "blk.14.attn_output.weight": "155d52efb23d366016d861a251d4d1f4a0c13699188c50d50dba016a0d8bfcd9", + "blk.14.attn_qkv.weight": "8e1415084e1f33c73a777f19e752489f4dd312cca047733e5ea643cd4a955e04", + "blk.14.ffn_down.weight": "a2a142226b94baa01ccb65bdea2b7418e49085c1d9c3c63e544e3112c58a25da", + "blk.14.ffn_norm.weight": "8aecfd9b0ae6affaea31a80c5c9a4a14b31deaa0db7bd8f6da2a64d23447921c", + "blk.14.ffn_up.weight": "0c1407237b8c1bd02f193346b5681926fe698a5055eac6a7450451b0f991707c", + "blk.15.attn_norm.weight": "e037bd19880bfa83d983200fb0c7866f8ad16c3ff5cc4b4f3a37ca7373870ff6", + "blk.15.attn_output.weight": "045fe4fc95cc129a1b92771b179c11b12845c4c088786c607f17bd98857e68e1", + "blk.15.attn_qkv.weight": "7621b7559705cab1d4dea1c69f76dbf9dc1c8837a203b656f484703b9c1b70ce", + "blk.15.ffn_down.weight": "7e5ac20e290bc60761e1cd972354fde225b7fa861048d44d9a0dd9b046d55f58", + "blk.15.ffn_norm.weight": "b6d830d88f1db1825687973c8c2b1a24c6fa84f07af8d0e3ef9c86009baca0b2", + "blk.15.ffn_up.weight": "dcda0957cd04fc45476774dba2bbf9aa89d6b05d5ca7b10ae6f73ad2c49b1cd3", + "blk.16.attn_norm.weight": "4ee9b70ba15cb2a08240f93990e90f5068c48fceb481f8e2186bec8b7214eb3f", + "blk.16.attn_output.weight": "315cfe5536658d2498192b2980eade15b2c9a4ff220e4011911457b1727fa103", + "blk.16.attn_qkv.weight": "3c8122e3ad637583b9dcde8ff3a323267d3014bb1f0f9771e5322260ca9ecc8d", + "blk.16.ffn_down.weight": "3b5fbebd5ee2b86cad96fb8a9b45a8770d08f82c1c8b74d7061e866f7020a18d", + "blk.16.ffn_norm.weight": "ffab69f20bda372de6e5878f0539163e2fc6ba113621ded95705fc3b1465c9f0", + "blk.16.ffn_up.weight": "0935ea3d258da42d6258406365f39f58ddaabfe97ea5977580db3635188f24a1", + "blk.17.attn_norm.weight": "f030441733f3d147b4a06a1eb4aeb8465c7c24d9c53bf4c48fe7e134d3629803", + "blk.17.attn_output.weight": "07a955ef09e8dc766ac0df647d0b2c69f23c4c69a7137654b4aad80303ed0eda", + "blk.17.attn_qkv.weight": "1c10688061e21e2fe12ad0cb54bf03895c1f83c3b0df743a42f548b52cbca1b2", + "blk.17.ffn_down.weight": "ebb9cc9836f41d88fdae2aa9a4355514e4edaec8d1577ffeb947a35204e77f52", + "blk.17.ffn_norm.weight": "50aff44f6528b13db5389f2ddcdb7676244947610bd7ffbff3f881c968c2a0d4", + "blk.17.ffn_up.weight": "d716537949582be33bde6b02e38f5a70081c9642a9fb05a61312126718b8d148", + "blk.18.attn_norm.weight": "0ea695c4e53d637902f46663a6ee42adc493c36794476acc7dbddaa05b13840d", + "blk.18.attn_output.weight": "5fd35b500221a612eb4f4bddf0e9b6b7db4d7733032a75f8802fb2d884647c2e", + "blk.18.attn_qkv.weight": "b0da37fd030fe69581f990bf23bfd35467a1bbe558af6de7c0924f6b72e92317", + "blk.18.ffn_down.weight": "b355c33f44b328f4bb977567de8f7544db4b005d7a8fbded658518ecf3c5a153", + "blk.18.ffn_norm.weight": "58b3fe9094079989a86e0387143259e1cc35952d24dc3df290c4ba6df44f5c51", + "blk.18.ffn_up.weight": "2ce530954c342c30ed2ead5353f931960bfae1d278868504c0efb973560fabbe", + "blk.19.attn_norm.weight": "533e9aed66feea8f0392aa81f9e293240e1f009a5334253915fb60c2749b615d", + "blk.19.attn_output.weight": "84f2d00f98a4113a779d3b5d1c3e7c914eb47784d3ab13b290367c124c2994aa", + "blk.19.attn_qkv.weight": "fbe6b9f53b07fa7537d3b3d452d20a9bc666f9fd41ec2091dd28bc2f70fc668f", + "blk.19.ffn_down.weight": "b30199e098c8bb3f890183d8b18471e80b62b604729b277ad62488dd71e1206b", + "blk.19.ffn_norm.weight": "c81373e41cd340b7badb19f9517c77c4250b4eb9a02dc758b8b49b652487d7ff", + "blk.19.ffn_up.weight": "5a5cb083ca7725720e3a890f7fa46354760e8007a8188849a092e305694a75e3", + "blk.20.attn_norm.weight": "4953091b4477e354357a8e743ba0a1900633e52f1599ee082a0c9b0b2b5cd978", + "blk.20.attn_output.weight": "62d54f7749cd6856097b2632066a322b0296df915fe66f382c5b5981be0d4f23", + "blk.20.attn_qkv.weight": "406de9e35b0729ebe902d7a47905cc7fb29a921431ed35dbef0c03e5690a1329", + "blk.20.ffn_down.weight": "62fb678b0d1261e19a4903a2b347d67afcc8acff01feb33a687a35a2d1e6f9a5", + "blk.20.ffn_norm.weight": "cd9d36b7e71e55c8925b97bb09c28219f182626bcff094878ae39c3db887a14b", + "blk.20.ffn_up.weight": "b9276771d79d3e932e73ccc520c3f8476342b9ef312ed2ee1e0da822e6e3ad18", + "blk.21.attn_norm.weight": "66d8c8a35e13ce9c2a0e75b670150e2c31484a55c2316df46075312196178ed3", + "blk.21.attn_output.weight": "12ab46c9382648f9b3350fdd92a6be6352743d62d6b520d7e2024e0c838588f5", + "blk.21.attn_qkv.weight": "a7909676ee1675ca23cd29a5fdd226df8dd9d68f94c6c9bbb51dd9fd38504008", + "blk.21.ffn_down.weight": "6fb317279c6542e82f97d5a12a60fac1bd0fa0405154f9fbe265e2fe39bd49cc", + "blk.21.ffn_norm.weight": "c0f703eb3ff161b5ba4490d87d8684b8a6c47a8f433e12f418333b9db439010a", + "blk.21.ffn_up.weight": "6dbdb80ef0c35e364bbce12d40d5e74c7963c7b55d58d9579567a07ffce7b863", + "blk.22.attn_norm.weight": "f94237433bf03d675cb2f655b81ca91a1ce2447bc6b00b13d6b0ccfe2d411eff", + "blk.22.attn_output.weight": "e821f95995ce497c01e63ca64f737713b1b65f11df1903e51d444aa516f33f71", + "blk.22.attn_qkv.weight": "1b0f717c73afb5eb4c82a1708c4e85c969e8a2a8770d9ddb78b1870a2d8a781e", + "blk.22.ffn_down.weight": "0f33f7a3cdc685484be99aa0c03642b0b20850a27d1fddbe054b13a9382f3ccb", + "blk.22.ffn_norm.weight": "9df285cf211ddd7df2b36a50489af574755c7d4d98b29a05cd04566ae613c8dc", + "blk.22.ffn_up.weight": "63ac300e1efb34041dd0136cf43ea622fac6f0caccce1cd9262f5e08d2cf179c", + "blk.23.attn_norm.weight": "5f72d9e88689b4027b28f5f8f26cd3abb03635ceea7ec98a4c91a9fc691f6707", + "blk.23.attn_output.weight": "6ecf04ff61125c5fc768f8656497152149373daf321ee9c957e8f7245a1184d1", + "blk.23.attn_qkv.weight": "a9d9978806724c2959f2cf386c233831f08e1e933dbf2b32665e788d9d512ea4", + "blk.23.ffn_down.weight": "72c7d17886a3da17fa0daa456aa5e877b2ef5b8b403182b870d9ca5ca9c70347", + "blk.23.ffn_norm.weight": "971e4b712e3025a13419b5b57d674b5e4ab7f18f74b57b9afc4671623da90c4b", + "blk.23.ffn_up.weight": "df2b5c7dbd5834545b815073af0c7355b065124e6d6f0fee78d8fa5b2076dc3e", + "blk.24.attn_norm.weight": "c41957c4a79ad3b16f6e11daec1c7f530b9f3f4b618e1e4367c3b67787ac4ab6", + "blk.24.attn_output.weight": "ef7d61f5fc88ac6f31bf60cb5f4d2d6b8df42d38825807112361a7224b0dee3b", + "blk.24.attn_qkv.weight": "3e6a58fe7d49c90bb6971efbad3371c32256881173ea5aee4b0c296cb206490f", + "blk.24.ffn_down.weight": "f43619144047de42fed81dfa495f1815d3cb771330e574043e2b67620819292c", + "blk.24.ffn_norm.weight": "5501d4a2a98c8ca6b42e77b53b221dbc08f530f6a067256d787534ec6fe028bd", + "blk.24.ffn_up.weight": "d64c8b0e509e2b1118f6000176f8956cacecdbb200c7e95ed93fb78b6e26c84a", + "blk.25.attn_norm.weight": "502fa3c302d371f61c5791f4615b73018ffb1daa09b6499b227116581244c5d4", + "blk.25.attn_output.weight": "ad8391d4e9c980856f2547aa945b2b6a407a6382158dc1ddd4f08d94ecc24be6", + "blk.25.attn_qkv.weight": "42e8983780d4a01a02c54ad23d4df21eea437f119a10af5a9c12a76a42d308c1", + "blk.25.ffn_down.weight": "302dd010d4e0ab4eeaee89090409ea0dddeeeed3236415eb8f97c942497eea91", + "blk.25.ffn_norm.weight": "fb34c1ee5bca96986c08834df0a0c047ba041c1123ac1f563e9d64312bf82d6a", + "blk.25.ffn_up.weight": "10739a8de156816d93c92b935386540bfa976bdbef204f0312960f6fc657582f", + "blk.26.attn_norm.weight": "7036c711609128c4e55968ff3681d3043338879a5737efd6c2ac9e1a2a61f1a0", + "blk.26.attn_output.weight": "db5db45dead5cb911fa01da59832f121b7c18b2d167bf53741c40819f24d346c", + "blk.26.attn_qkv.weight": "cae34c6b7f82ed14348d5ed30a79919c383737c1694a9cb9c0de609d3b0c1d0a", + "blk.26.ffn_down.weight": "491ec3a4da9b4f49f8ebc6be658ce397a9b801ae9fb35e82177e47808c65e5d0", + "blk.26.ffn_norm.weight": "fd7059d75d7f0e5288511ddeeb0f772eb3cae3ccfe4226b877015834edc3c386", + "blk.26.ffn_up.weight": "ea1ee1274c56458ce056d2205e5bb6e5422ce4cb0ad58006b8141749b97a0c39", + "blk.27.attn_norm.weight": "cc362c9a937609265052cd38544af17a1a7448cea086d4c801139e1fc865832d", + "blk.27.attn_output.weight": "ba757a81dabde9cb1b069d1bb616fe79649a1724f756567ec61caed1304fe6cf", + "blk.27.attn_qkv.weight": "1ab8d7d02d87756c12c2275636823aa5ede3d683178225c4cac4bd892c319bd4", + "blk.27.ffn_down.weight": "deb1c711c8a66acf4dcd2d088e1548f8e08f296f755e4067d6557fa55afde88c", + "blk.27.ffn_norm.weight": "fc6242d8cb8a4a37a8ddb7e41e7e60a63d4a89edf36acb35df052f10b9c91ece", + "blk.27.ffn_up.weight": "8df39b09c4801f343aca78f2918a1f6db78c8c55e591eda4c69eadb74c26e180", + "blk.28.attn_norm.weight": "75b539308f77e3cefdc6d98484d8b5cbf0538f0c2869a77b7373a145a18bc850", + "blk.28.attn_output.weight": "ae128940eb60a6d2e121762ef4b3e9dcf9eb3e105b249507fa7f12de0e19822c", + "blk.28.attn_qkv.weight": "bdda781c288e9326c240e33905f8e621b6a2ad902e620739d34f93fcd6f933de", + "blk.28.ffn_down.weight": "f1d6e6d1c286b1138bfd7e53fe477f399ae93bc2c04e35416f84218ed7247965", + "blk.28.ffn_norm.weight": "3f837ce82c8b9bde0d61d08b6f5fe5574886ea5328dbdc53f2929f18da8b4087", + "blk.28.ffn_up.weight": "2af027002e31d1b6cfedbdb30a2b9d7213f3aa691167c353913adfd48fda31e4", + "blk.29.attn_norm.weight": "61e8003b5329462ffe0fe172f2b160260de006aed858332d49d75504b6b6aa7a", + "blk.29.attn_output.weight": "ca44542a72a37476dc73dbdcc01f5b7497cb3ebc4ea230a55c9634ccd8e56ad4", + "blk.29.attn_qkv.weight": "abb3d9d6abe57872ae3daa51935d43264093ded5ce63b49d1e280ee5758be0e4", + "blk.29.ffn_down.weight": "6764b895fce881df097489c263446f0106de36217997660c15984b3ee22a5a06", + "blk.29.ffn_norm.weight": "89e03e9a33fc0e6e31ba9f0c2bd7c5734a118c5602bb90148793e08a80e8d0ae", + "blk.29.ffn_up.weight": "fa7ad57a84954f4121653152efed1a871d8adb20a1ea9086e3e849ce359d7d2e", + "blk.30.attn_norm.weight": "91a697aca1e42af54f806a20211031c3369e8d0bd58df1b0147fe24954e1f5a4", + "blk.30.attn_output.weight": "36063fcf766c89ac75be56f688cc63cefe5f2c733fbf4378ea9956ad386fa148", + "blk.30.attn_qkv.weight": "2cacd1161f1121a2c0b979930134f4666f73fb8d7237b3b0659ae091b15955a6", + "blk.30.ffn_down.weight": "9f3fcb6217100595850c05dc98f9ab2a263afdb6ab28df2fcb08aeff512057d7", + "blk.30.ffn_norm.weight": "6c600bc1fc7de39d4f8917b81fc7d1d5ed2a9b56492234c13a4bd6028c30d880", + "blk.30.ffn_up.weight": "73cabd1bb011956b2689ea3338bb76642ef3a57c197377d666d2ab5f56317668", + "blk.31.attn_norm.weight": "72d3e1cc771380645fa75a899858c95f39857a4f3f1ed60fe1578df383b8bc53", + "blk.31.attn_output.weight": "40089cdd29994dc19a1d89fa15902a89cfeca3540f12dc9bf4d00ef82506e456", + "blk.31.attn_qkv.weight": "1d0bb40e9258071ae14290a53c619a8e331dda07354d2a02ef45766c029ae5e4", + "blk.31.ffn_down.weight": "8defa0e06335b793fa8be03883f0a322d6c5b33f52c69c943c35c60d16e42c0a", + "blk.31.ffn_norm.weight": "33c55d9d0c496ccfb130361fe131649346e098abaaac39c0519507e5d846721d", + "blk.31.ffn_up.weight": "599f6503f61c692c1f82001973d35119f9688db5e6be9d9c298411491c93f09b", + "output.weight": "14b8dc662bfa3308ebb2e102c562d8e52c15670e538f20f3216a9c310ca9dd41", + "output_norm.weight": "7f2294ba94ce65681df6c7ddd8698799199b9d77dc83c10bdad5c3999f0fdb82", + "rope_factors_long.weight": "e34d378664e354652c38f47d10dafb0498ccc2fb042d39ff7fef768146fff22b", + "rope_factors_short.weight": "9379146a4988f373d362fe47b06c75e7fe7c54aa4dc9558758df79b7a87471fd", + "token_embd.weight": "19a03c1fb5ac0baee93b0a7d8b0f26e9a9b011e229b694afc50ebfc13d84f8bf" +} diff --git a/llm/ggml.go b/llm/ggml.go index d7f2eef7..4c68adf9 100644 --- a/llm/ggml.go +++ b/llm/ggml.go @@ -157,6 +157,14 @@ type Tensor struct { io.WriterTo `json:"-"` } +func (t Tensor) block() (n int) { + if _, err := fmt.Sscanf(t.Name, "blk.%d.", &n); err != nil { + return -1 + } + + return +} + func (t Tensor) blockSize() uint64 { switch t.Kind { case 0, 1, 24, 25, 26, 27, 28, 30: // F32, F16, I8, I16, I32, I64, F64, BF16 diff --git a/llm/gguf.go b/llm/gguf.go index 98158313..2e6bc542 100644 --- a/llm/gguf.go +++ b/llm/gguf.go @@ -532,15 +532,14 @@ func WriteGGUF(ws io.WriteSeeker, kv KV, ts []Tensor) error { } } - slices.SortFunc(ts, func(a, b Tensor) int { - var i, j int - if n, err := fmt.Sscanf(a.Name, "blk.%d", &i); err != nil || n != 1 { - return cmp.Compare(a.Name, b.Name) - } else if n, err := fmt.Sscanf(b.Name, "blk.%d", &j); err != nil || n != 1 { - return cmp.Compare(a.Name, b.Name) + slices.SortStableFunc(ts, func(a, b Tensor) int { + if i, j := a.block(), b.block(); i < 0 && j > 0 { + return 1 + } else if i > 0 && j < 0 { + return -1 + } else { + return cmp.Compare(i, j) } - - return cmp.Compare(i, j) }) var s uint64 From aec77d6a05c3cd13732eab7decc9794bbed670d9 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Tue, 2 Jul 2024 14:40:01 -0700 Subject: [PATCH 027/174] support new "longrope" attention factor --- convert/convert_phi3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert/convert_phi3.go b/convert/convert_phi3.go index 7aa3ed15..0f645217 100644 --- a/convert/convert_phi3.go +++ b/convert/convert_phi3.go @@ -58,7 +58,7 @@ func (p *phi3) KV(t *Tokenizer) llm.KV { switch p.RopeScaling.Type { case "": // no scaling - case "su": + case "su", "longrope": kv["phi3.rope.scaling.attn_factor"] = float32(max(math.Sqrt(1+math.Log(scale)/math.Log(float64(p.OriginalMaxPositionEmbeddings))), 1.0)) case "yarn": kv["phi3.rope.scaling.attn_factor"] = float32(max(0.1*math.Log(scale)+1.0, 1.0)) From bd5e432630a0c1d1ca5795052355a45014e71a2a Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Mon, 5 Aug 2024 10:30:32 -0700 Subject: [PATCH 028/174] update import.md --- docs/import.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/import.md b/docs/import.md index f34f09ac..82ea9ba5 100644 --- a/docs/import.md +++ b/docs/import.md @@ -16,7 +16,9 @@ If the model being imported is one of these architectures, it can be imported di - LlamaForCausalLM - MistralForCausalLM + - MixtralForCausalLM - GemmaForCausalLM + - Phi3ForCausalLM ```dockerfile FROM /path/to/safetensors/directory From 8b00a415ab5170a5a75b105402ca262d1fb7ac12 Mon Sep 17 00:00:00 2001 From: royjhan <65097070+royjhan@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:19:56 -0400 Subject: [PATCH 029/174] Load Embedding Model on Empty Input (#6325) * load on empty input * no load on invalid input --- server/routes.go | 16 +++++----- server/routes_test.go | 70 ------------------------------------------- 2 files changed, 9 insertions(+), 77 deletions(-) diff --git a/server/routes.go b/server/routes.go index e5a31002..6c470c17 100644 --- a/server/routes.go +++ b/server/routes.go @@ -324,13 +324,10 @@ func (s *Server) EmbedHandler(c *gin.Context) { input = append(input, v.(string)) } default: - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid input type"}) - return - } - - if len(input) == 0 { - c.JSON(http.StatusOK, api.EmbedResponse{Model: req.Model, Embeddings: [][]float32{}}) - return + if req.Input != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid input type"}) + return + } } r, m, opts, err := s.scheduleRunner(c.Request.Context(), req.Model, []Capability{}, req.Options, req.KeepAlive) @@ -341,6 +338,11 @@ func (s *Server) EmbedHandler(c *gin.Context) { checkpointLoaded := time.Now() + if len(input) == 0 { + c.JSON(http.StatusOK, api.EmbedResponse{Model: req.Model, Embeddings: [][]float32{}}) + return + } + kvData, err := getKVData(m.ModelPath, false) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) diff --git a/server/routes_test.go b/server/routes_test.go index ef7248ef..242875d6 100644 --- a/server/routes_test.go +++ b/server/routes_test.go @@ -272,76 +272,6 @@ func Test_Routes(t *testing.T) { assert.Equal(t, "library", retrieveResp.OwnedBy) }, }, - { - Name: "Embed Handler Empty Input", - Method: http.MethodPost, - Path: "/api/embed", - Setup: func(t *testing.T, req *http.Request) { - embedReq := api.EmbedRequest{ - Model: "t-bone", - Input: "", - } - jsonData, err := json.Marshal(embedReq) - require.NoError(t, err) - req.Body = io.NopCloser(bytes.NewReader(jsonData)) - }, - Expected: func(t *testing.T, resp *http.Response) { - contentType := resp.Header.Get("Content-Type") - if contentType != "application/json; charset=utf-8" { - t.Fatalf("expected content type application/json; charset=utf-8, got %s", contentType) - } - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - - var embedResp api.EmbedResponse - err = json.Unmarshal(body, &embedResp) - if err != nil { - t.Fatal(err) - } - - if embedResp.Model != "t-bone" { - t.Fatalf("expected model t-bone, got %s", embedResp.Model) - } - - if embedResp.Embeddings == nil { - t.Fatalf("expected embeddings to not be nil, got %v", embedResp.Embeddings) - } - - if len(embedResp.Embeddings) != 0 { - t.Fatalf("expected embeddings to be empty, got %v", embedResp.Embeddings) - } - }, - }, - { - Name: "Embed Handler Invalid Input", - Method: http.MethodPost, - Path: "/api/embed", - Setup: func(t *testing.T, req *http.Request) { - embedReq := api.EmbedRequest{ - Model: "t-bone", - Input: 2, - } - jsonData, err := json.Marshal(embedReq) - require.NoError(t, err) - req.Body = io.NopCloser(bytes.NewReader(jsonData)) - }, - Expected: func(t *testing.T, resp *http.Response) { - contentType := resp.Header.Get("Content-Type") - if contentType != "application/json; charset=utf-8" { - t.Fatalf("expected content type application/json; charset=utf-8, got %s", contentType) - } - _, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - - if resp.StatusCode != http.StatusBadRequest { - t.Fatalf("expected status code 400, got %d", resp.StatusCode) - } - }, - }, } t.Setenv("OLLAMA_MODELS", t.TempDir()) From feedf49c717a449cedbf973b06ca97796cfaa004 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 13 Aug 2024 11:44:50 -0700 Subject: [PATCH 030/174] Go back to a pinned Go version Go version 1.22.6 is triggering AV false positives, so go back to 1.22.5 --- .github/workflows/release.yaml | 10 +++++----- .github/workflows/test.yaml | 10 +++++----- go.mod | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f0c6db5d..5ae630c3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -31,7 +31,7 @@ jobs: security set-keychain-settings -lut 3600 build.keychain - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - name: Build Darwin env: @@ -87,7 +87,7 @@ jobs: write-host "plugin installed" - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - run: go get ./... - run: | @@ -141,7 +141,7 @@ jobs: write-host "plugin installed" - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - name: 'Install ROCm' run: | @@ -218,7 +218,7 @@ jobs: write-host "plugin installed" - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - name: 'Install CUDA' run: | @@ -306,7 +306,7 @@ jobs: write-host "plugin installed" - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - run: go get - uses: actions/download-artifact@v4 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a57d45fd..3d58fa3e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -63,7 +63,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - run: go get ./... - run: | @@ -163,7 +163,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - name: 'Install ROCm' run: | @@ -200,7 +200,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - name: 'Install CUDA' run: | @@ -255,7 +255,7 @@ jobs: submodules: recursive - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: false - run: | case ${{ matrix.arch }} in @@ -297,7 +297,7 @@ jobs: submodules: recursive - uses: actions/setup-go@v5 with: - go-version: "stable" + go-version-file: go.mod cache: true - run: | case ${{ matrix.arch }} in diff --git a/go.mod b/go.mod index 2e0c6614..6e437c73 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ollama/ollama -go 1.22.0 +go 1.22.5 require ( github.com/containerd/console v1.0.3 From 1f32276178d5860bbaeafb7dd73d4ef93053bc15 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 13 Aug 2024 13:36:05 -0700 Subject: [PATCH 031/174] Update openai.md to remove extra checkbox (#6345) --- docs/openai.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/openai.md b/docs/openai.md index 7b3a3f31..75d2c595 100644 --- a/docs/openai.md +++ b/docs/openai.md @@ -182,7 +182,6 @@ curl http://localhost:11434/v1/embeddings \ - [x] Reproducible outputs - [x] Vision - [x] Tools (streaming support coming soon) -- [ ] Vision - [ ] Logprobs #### Supported request fields From 2697d7f5aad27248aebbe5acff1dcbede5367b7b Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 13 Aug 2024 13:40:37 -0700 Subject: [PATCH 032/174] lint - fixes printf: non-constant format string in call to fmt.Printf - fixes SA1032: arguments have the wrong order - disables testifylint --- .golangci.yaml | 1 - readline/buffer.go | 79 ++++++++++++++++---------------------------- readline/readline.go | 2 +- readline/types.go | 61 ++++++++++++++++++++++------------ server/sched.go | 2 +- 5 files changed, 70 insertions(+), 75 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index c2c8b52b..c9c9f620 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -24,7 +24,6 @@ linters: - nosprintfhostport - staticcheck - tenv - - testifylint - unconvert - unused - usestdlibvars diff --git a/readline/buffer.go b/readline/buffer.go index 68573d40..d91fe0a9 100644 --- a/readline/buffer.go +++ b/readline/buffer.go @@ -62,7 +62,7 @@ func (b *Buffer) MoveLeft() { rLength := runewidth.RuneWidth(r) if b.DisplayPos%b.LineWidth == 0 { - fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width)) + fmt.Print(CursorUp + CursorBOL + CursorRightN(b.Width)) if rLength == 2 { fmt.Print(CursorLeft) } @@ -74,7 +74,7 @@ func (b *Buffer) MoveLeft() { fmt.Print(CursorLeft) } } else { - fmt.Print(cursorLeftN(rLength)) + fmt.Print(CursorLeftN(rLength)) } b.Pos -= 1 @@ -115,15 +115,15 @@ func (b *Buffer) MoveRight() { b.DisplayPos += rLength if b.DisplayPos%b.LineWidth == 0 { - fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt()))) + fmt.Print(CursorDown + CursorBOL + CursorRightN(len(b.Prompt.prompt()))) } else if (b.DisplayPos-rLength)%b.LineWidth == b.LineWidth-1 && hasSpace { - fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt())+rLength)) + fmt.Print(CursorDown + CursorBOL + CursorRightN(len(b.Prompt.prompt())+rLength)) b.DisplayPos += 1 } else if b.LineHasSpace.Size() > 0 && b.DisplayPos%b.LineWidth == b.LineWidth-1 && hasSpace { - fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt()))) + fmt.Print(CursorDown + CursorBOL + CursorRightN(len(b.Prompt.prompt()))) b.DisplayPos += 1 } else { - fmt.Print(cursorRightN(rLength)) + fmt.Print(CursorRightN(rLength)) } } } @@ -154,7 +154,7 @@ func (b *Buffer) MoveToStart() { fmt.Print(CursorUp) } } - fmt.Printf(CursorBOL + cursorRightN(len(b.Prompt.prompt()))) + fmt.Print(CursorBOL + CursorRightN(len(b.Prompt.prompt()))) b.Pos = 0 b.DisplayPos = 0 } @@ -169,9 +169,9 @@ func (b *Buffer) MoveToEnd() { fmt.Print(CursorDown) } remainder := b.DisplaySize() % b.LineWidth - fmt.Printf(CursorBOL + cursorRightN(len(b.Prompt.prompt())+remainder)) + fmt.Print(CursorBOL + CursorRightN(len(b.Prompt.prompt())+remainder)) } else { - fmt.Print(cursorRightN(b.DisplaySize() - b.DisplayPos)) + fmt.Print(CursorRightN(b.DisplaySize() - b.DisplayPos)) } b.Pos = b.Buf.Size() @@ -286,8 +286,7 @@ func (b *Buffer) drawRemaining() { remLength := runewidth.StringWidth(remainingText) if len(currLine) > 0 { - fmt.Printf(ClearToEOL + currLine) - fmt.Print(cursorLeftN(currLineSpace)) + fmt.Print(ClearToEOL + currLine + CursorLeftN(currLineSpace)) } else { fmt.Print(ClearToEOL) } @@ -301,9 +300,9 @@ func (b *Buffer) drawRemaining() { } if (b.DisplayPos+currLineSpace)%b.LineWidth == 0 && currLine == remainingText { - fmt.Print(cursorRightN(currLineSpace)) + fmt.Print(CursorRightN(currLineSpace)) fmt.Printf("\n%s", b.Prompt.AltPrompt) - fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width-currLineSpace)) + fmt.Print(CursorUp + CursorBOL + CursorRightN(b.Width-currLineSpace)) } // render the other lines @@ -333,9 +332,7 @@ func (b *Buffer) drawRemaining() { lineLength += runewidth.RuneWidth(c) fmt.Printf("%c", c) } - fmt.Print(ClearToEOL) - fmt.Print(cursorUpN(totalLines)) - fmt.Printf(CursorBOL + cursorRightN(b.Width-currLineSpace)) + fmt.Print(ClearToEOL + CursorUpN(totalLines) + CursorBOL + CursorRightN(b.Width-currLineSpace)) hasSpace := b.GetLineSpacing(b.DisplayPos / b.LineWidth) @@ -357,8 +354,7 @@ func (b *Buffer) Remove() { if b.DisplayPos%b.LineWidth == 0 { // if the user backspaces over the word boundary, do this magic to clear the line // and move to the end of the previous line - fmt.Printf(CursorBOL + ClearToEOL) - fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width)) + fmt.Print(CursorBOL + ClearToEOL + CursorUp + CursorBOL + CursorRightN(b.Width)) if b.DisplaySize()%b.LineWidth < (b.DisplaySize()-rLength)%b.LineWidth { b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1) @@ -370,24 +366,23 @@ func (b *Buffer) Remove() { } if rLength == 2 { - fmt.Print(CursorLeft + " " + cursorLeftN(2)) + fmt.Print(CursorLeft + " " + CursorLeftN(2)) } else { fmt.Print(" " + CursorLeft) } } else if (b.DisplayPos-rLength)%b.LineWidth == 0 && hasSpace { - fmt.Printf(CursorBOL + ClearToEOL) - fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width)) + fmt.Print(CursorBOL + ClearToEOL + CursorUp + CursorBOL + CursorRightN(b.Width)) if b.Pos == b.Buf.Size() { b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1) } b.DisplayPos -= 1 } else { - fmt.Print(cursorLeftN(rLength)) + fmt.Print(CursorLeftN(rLength)) for range rLength { fmt.Print(" ") } - fmt.Print(cursorLeftN(rLength)) + fmt.Print(CursorLeftN(rLength)) } var eraseExtraLine bool @@ -405,9 +400,9 @@ func (b *Buffer) Remove() { // are trailing characters which go over the line width boundary if eraseExtraLine { remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth - fmt.Printf(cursorDownN(remainingLines+1) + CursorBOL + ClearToEOL) + fmt.Print(CursorDownN(remainingLines+1) + CursorBOL + ClearToEOL) place := b.DisplayPos % b.LineWidth - fmt.Printf(cursorUpN(remainingLines+1) + cursorRightN(place+len(b.Prompt.prompt()))) + fmt.Print(CursorUpN(remainingLines+1) + CursorRightN(place+len(b.Prompt.prompt()))) } } } @@ -422,9 +417,9 @@ func (b *Buffer) Delete() { if b.DisplaySize()%b.LineWidth == 0 { if b.DisplayPos != b.DisplaySize() { remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth - fmt.Printf(cursorDownN(remainingLines) + CursorBOL + ClearToEOL) + fmt.Print(CursorDownN(remainingLines) + CursorBOL + ClearToEOL) place := b.DisplayPos % b.LineWidth - fmt.Printf(cursorUpN(remainingLines) + cursorRightN(place+len(b.Prompt.prompt()))) + fmt.Print(CursorUpN(remainingLines) + CursorRightN(place+len(b.Prompt.prompt()))) } } } @@ -471,17 +466,17 @@ func (b *Buffer) DeleteWord() { } func (b *Buffer) ClearScreen() { - fmt.Printf(ClearScreen + CursorReset + b.Prompt.prompt()) + fmt.Print(ClearScreen + CursorReset + b.Prompt.prompt()) if b.IsEmpty() { ph := b.Prompt.placeholder() - fmt.Printf(ColorGrey + ph + cursorLeftN(len(ph)) + ColorDefault) + fmt.Print(ColorGrey + ph + CursorLeftN(len(ph)) + ColorDefault) } else { currPos := b.DisplayPos currIndex := b.Pos b.Pos = 0 b.DisplayPos = 0 b.drawRemaining() - fmt.Printf(CursorReset + cursorRightN(len(b.Prompt.prompt()))) + fmt.Print(CursorReset + CursorRightN(len(b.Prompt.prompt()))) if currPos > 0 { targetLine := currPos / b.LineWidth if targetLine > 0 { @@ -491,10 +486,10 @@ func (b *Buffer) ClearScreen() { } remainder := currPos % b.LineWidth if remainder > 0 { - fmt.Print(cursorRightN(remainder)) + fmt.Print(CursorRightN(remainder)) } if currPos%b.LineWidth == 0 { - fmt.Printf(CursorBOL + b.Prompt.AltPrompt) + fmt.Print(CursorBOL + b.Prompt.AltPrompt) } } b.Pos = currIndex @@ -513,13 +508,13 @@ func (b *Buffer) Replace(r []rune) { b.Buf.Clear() - fmt.Printf(CursorBOL + ClearToEOL) + fmt.Print(CursorBOL + ClearToEOL) for range lineNums { fmt.Print(CursorUp + CursorBOL + ClearToEOL) } - fmt.Printf(CursorBOL + b.Prompt.prompt()) + fmt.Print(CursorBOL + b.Prompt.prompt()) for _, c := range r { b.Add(c) @@ -545,19 +540,3 @@ func (b *Buffer) StringNM(n, m int) string { } return s } - -func cursorLeftN(n int) string { - return fmt.Sprintf(CursorLeftN, n) -} - -func cursorRightN(n int) string { - return fmt.Sprintf(CursorRightN, n) -} - -func cursorUpN(n int) string { - return fmt.Sprintf(CursorUpN, n) -} - -func cursorDownN(n int) string { - return fmt.Sprintf(CursorDownN, n) -} diff --git a/readline/readline.go b/readline/readline.go index e90a5e01..1c14fe10 100644 --- a/readline/readline.go +++ b/readline/readline.go @@ -98,7 +98,7 @@ func (i *Instance) Readline() (string, error) { showPlaceholder := !i.Pasting || i.Prompt.UseAlt if buf.IsEmpty() && showPlaceholder { ph := i.Prompt.placeholder() - fmt.Printf(ColorGrey + ph + fmt.Sprintf(CursorLeftN, len(ph)) + ColorDefault) + fmt.Print(ColorGrey + ph + CursorLeftN(len(ph)) + ColorDefault) } r, err := i.Terminal.Read() diff --git a/readline/types.go b/readline/types.go index 3b88588f..e136d996 100644 --- a/readline/types.go +++ b/readline/types.go @@ -1,5 +1,7 @@ package readline +import "strconv" + const ( CharNull = 0 CharLineStart = 1 @@ -41,34 +43,49 @@ const ( ) const ( - CursorUp = "\033[1A" - CursorDown = "\033[1B" - CursorRight = "\033[1C" - CursorLeft = "\033[1D" + Esc = "\x1b" - CursorSave = "\033[s" - CursorRestore = "\033[u" + CursorSave = Esc + "[s" + CursorRestore = Esc + "[u" - CursorUpN = "\033[%dA" - CursorDownN = "\033[%dB" - CursorRightN = "\033[%dC" - CursorLeftN = "\033[%dD" + CursorEOL = Esc + "[E" + CursorBOL = Esc + "[1G" + CursorHide = Esc + "[?25l" + CursorShow = Esc + "[?25h" - CursorEOL = "\033[E" - CursorBOL = "\033[1G" - CursorHide = "\033[?25l" - CursorShow = "\033[?25h" + ClearToEOL = Esc + "[K" + ClearLine = Esc + "[2K" + ClearScreen = Esc + "[2J" + CursorReset = Esc + "[0;0f" - ClearToEOL = "\033[K" - ClearLine = "\033[2K" - ClearScreen = "\033[2J" - CursorReset = "\033[0;0f" + ColorGrey = Esc + "[38;5;245m" + ColorDefault = Esc + "[0m" - ColorGrey = "\033[38;5;245m" - ColorDefault = "\033[0m" + StartBracketedPaste = Esc + "[?2004h" + EndBracketedPaste = Esc + "[?2004l" +) - StartBracketedPaste = "\033[?2004h" - EndBracketedPaste = "\033[?2004l" +func CursorUpN(n int) string { + return Esc + "[" + strconv.Itoa(n) + "A" +} + +func CursorDownN(n int) string { + return Esc + "[" + strconv.Itoa(n) + "B" +} + +func CursorRightN(n int) string { + return Esc + "[" + strconv.Itoa(n) + "C" +} + +func CursorLeftN(n int) string { + return Esc + "[" + strconv.Itoa(n) + "D" +} + +var ( + CursorUp = CursorUpN(1) + CursorDown = CursorDownN(1) + CursorRight = CursorRightN(1) + CursorLeft = CursorLeftN(1) ) const ( diff --git a/server/sched.go b/server/sched.go index c378865b..9947fd32 100644 --- a/server/sched.go +++ b/server/sched.go @@ -418,7 +418,7 @@ func (s *Scheduler) load(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, // some older models are not compatible with newer versions of llama.cpp // show a generalized compatibility error until there is a better way to // check for model compatibility - if errors.Is(llm.ErrUnsupportedFormat, err) || strings.Contains(err.Error(), "failed to load model") { + if errors.Is(err, llm.ErrUnsupportedFormat) || strings.Contains(err.Error(), "failed to load model") { err = fmt.Errorf("%v: this model may be incompatible with your version of Ollama. If you previously pulled this model, try updating it by running `ollama pull %s`", err, req.model.ShortName) } slog.Info("NewLlamaServer failed", "model", req.model.ModelPath, "error", err) From eda8a32a0936c1aec120b3c544e402cbba7b7eb7 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Tue, 13 Aug 2024 23:39:18 +0000 Subject: [PATCH 033/174] update chatml template format to latest in docs (#6344) --- docs/template.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/template.md b/docs/template.md index f6ce06ba..1d7104de 100644 --- a/docs/template.md +++ b/docs/template.md @@ -112,15 +112,9 @@ Keep the following tips and best practices in mind when working with Go template ChatML is a popular template format. It can be used for models such as Databrick's DBRX, Intel's Neural Chat, and Microsoft's Orca 2. ```gotmpl -{{- if .System }}<|im_start|>system -{{ .System }}<|im_end|> -{{ end }} {{- range .Messages }}<|im_start|>{{ .Role }} {{ .Content }}<|im_end|> {{ end }}<|im_start|>assistant -{{ else }} -{{ if .System }}<|im_start|>system -{{ .System }}<|im_end|> ``` ### Example Tools From 8e1050f366e5451651f8385fa570b78b9c7d21cc Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 13 Aug 2024 16:47:35 -0700 Subject: [PATCH 034/174] server: reduce max connections used in download (#6347) The previous value of 64 was WAY too high and unnecessary. It reached diminishing returns and blew past it. This is a more reasonable number for _most_ normal cases. For users on cloud servers with excellent network quality, this will keep screaming for them, without hitting our CDN limits. For users with relatively poor network quality, this will keep them from saturating their network and causing other issues. --- server/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/download.go b/server/download.go index 1bca86bf..02f7ae88 100644 --- a/server/download.go +++ b/server/download.go @@ -94,7 +94,7 @@ func (p *blobDownloadPart) UnmarshalJSON(b []byte) error { } const ( - numDownloadParts = 64 + numDownloadParts = 16 minDownloadPartSize int64 = 100 * format.MegaByte maxDownloadPartSize int64 = 1000 * format.MegaByte ) From 0a8d6ea86d54bbda9d701c38e4279a9c5c204cd9 Mon Sep 17 00:00:00 2001 From: longtao <39115651+eust-w@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:54:19 +0800 Subject: [PATCH 035/174] Fix typo and improve readability (#5964) * Fix typo and improve readability Summary: * Rename updatAvailableMenuID to updateAvailableMenuID * Replace unused cmd parameter with _ in RunServer function * Fix typos in comments (cherry picked from commit 5b8715f0b04773369e8eb1f9e6737995a0ab3ba7) * Update api/client.go Co-authored-by: Jeffrey Morgan --------- Co-authored-by: Jeffrey Morgan --- api/client.go | 4 ++-- app/tray/wintray/menus.go | 14 +++++++------- cmd/cmd.go | 2 +- types/model/name.go | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/client.go b/api/client.go index bbdf8202..2528fb21 100644 --- a/api/client.go +++ b/api/client.go @@ -298,7 +298,7 @@ func (c *Client) List(ctx context.Context) (*ListResponse, error) { return &lr, nil } -// List running models. +// ListRunning lists running models. func (c *Client) ListRunning(ctx context.Context) (*ProcessResponse, error) { var lr ProcessResponse if err := c.do(ctx, http.MethodGet, "/api/ps", nil, &lr); err != nil { @@ -333,7 +333,7 @@ func (c *Client) Show(ctx context.Context, req *ShowRequest) (*ShowResponse, err return &resp, nil } -// Hearbeat checks if the server has started and is responsive; if yes, it +// Heartbeat checks if the server has started and is responsive; if yes, it // returns nil, otherwise an error. func (c *Client) Heartbeat(ctx context.Context) error { if err := c.do(ctx, http.MethodHead, "/", nil, nil); err != nil { diff --git a/app/tray/wintray/menus.go b/app/tray/wintray/menus.go index 9cb3b893..59624444 100644 --- a/app/tray/wintray/menus.go +++ b/app/tray/wintray/menus.go @@ -11,12 +11,12 @@ import ( ) const ( - updatAvailableMenuID = 1 - updateMenuID = updatAvailableMenuID + 1 - separatorMenuID = updateMenuID + 1 - diagLogsMenuID = separatorMenuID + 1 - diagSeparatorMenuID = diagLogsMenuID + 1 - quitMenuID = diagSeparatorMenuID + 1 + updateAvailableMenuID = 1 + updateMenuID = updateAvailableMenuID + 1 + separatorMenuID = updateMenuID + 1 + diagLogsMenuID = separatorMenuID + 1 + diagSeparatorMenuID = diagLogsMenuID + 1 + quitMenuID = diagSeparatorMenuID + 1 ) func (t *winTray) initMenus() error { @@ -35,7 +35,7 @@ func (t *winTray) initMenus() error { func (t *winTray) UpdateAvailable(ver string) error { if !t.updateNotified { slog.Debug("updating menu and sending notification for new update") - if err := t.addOrUpdateMenuItem(updatAvailableMenuID, 0, updateAvailableMenuTitle, true); err != nil { + if err := t.addOrUpdateMenuItem(updateAvailableMenuID, 0, updateAvailableMenuTitle, true); err != nil { return fmt.Errorf("unable to create menu entries %w", err) } if err := t.addOrUpdateMenuItem(updateMenuID, 0, updateMenutTitle, false); err != nil { diff --git a/cmd/cmd.go b/cmd/cmd.go index 2356110e..fd7246c8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1125,7 +1125,7 @@ func generate(cmd *cobra.Command, opts runOptions) error { return nil } -func RunServer(cmd *cobra.Command, _ []string) error { +func RunServer(_ *cobra.Command, _ []string) error { if err := initializeKeypair(); err != nil { return err } diff --git a/types/model/name.go b/types/model/name.go index 018cb2f5..75b35ef7 100644 --- a/types/model/name.go +++ b/types/model/name.go @@ -219,7 +219,7 @@ func (n Name) String() string { return b.String() } -// DisplayShort returns a short string version of the name. +// DisplayShortest returns a short string version of the name. func (n Name) DisplayShortest() string { var sb strings.Builder From 8200c371aed68dec5c74e869491ee8e5749ba1eb Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 14 Aug 2024 15:19:50 -0700 Subject: [PATCH 036/174] add `CONTRIBUTING.md` (#6349) --- CONTRIBUTING.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..7f12a0fc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to Ollama + +Thank you for your interest in contributing to Ollama! Here are a few guidelines to help get you started. + +## Set up + +See the [development documentation](./docs/development.md) for instructions on how to build and run Ollama locally. + +## Pull requests + +### Ideal issues + +* [Bugs](https://github.com/ollama/ollama/issues?q=is%3Aissue+is%3Aopen+label%3Abug): issues where Ollama stops working or where it results in an unexpected error. +* [Performance](https://github.com/ollama/ollama/issues?q=is%3Aissue+is%3Aopen+label%3Aperformance): issues to make Ollama faster at model inference, downloading or uploading. +* [Security](https://github.com/ollama/ollama/blob/main/SECURITY.md): issues that could lead to a security vulnerability. As mentioned in [SECURITY.md](https://github.com/ollama/ollama/blob/main/SECURITY.md), please do not disclose security vulnerabilities publicly. + +### Issues that are harder to review + +* New features: new features (e.g. API fields, environment variables) add surface area to Ollama and make it harder to maintain in the long run as they cannot be removed without potentially breaking users in the future. +* Refactoring: large code improvements are important, but can be harder or take longer to review and merge. +* Documentation: small updates to fill in or dorrect missing documentation is helpful, however large documentation additions can be hard to maintain over time. + +### Issues that may not be accepted + +* Changes that break backwards compatibility in Ollama's API (including the OpenAI-compatible API) +* Changes that add significant friction to the user experience +* Changes that create a large future maintenance burden for maintainers and contributors + +### Best practices + +* Commit messages: please leave both a title and a description in your commit messages. The title should be a short summary of the changes, with a leading word that explains the section of the code being changed (e.g. `api: fix parsing of prompt field`) . In the description, leave a short 2-3 sentences that explain more about the change and its impact. +* Tests: please add test coverage to changes where possible. +* Minimize dependencies: avoid adding new dependencies unless absolutely necessary. + +## Need help? + +If you need help with anything, feel free to reach out to us on our [Discord server](https://discord.gg/ollama). From b3f75fc812fc1559090a7fd9739bd203817a5979 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Wed, 14 Aug 2024 14:37:51 -0700 Subject: [PATCH 037/174] fix noprune --- server/images.go | 63 ++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/server/images.go b/server/images.go index 0e753f56..798ed818 100644 --- a/server/images.go +++ b/server/images.go @@ -215,25 +215,20 @@ func GetManifest(mp ModelPath) (*Manifest, string, error) { return nil, "", err } - if _, err = os.Stat(fp); err != nil { - return nil, "", err - } - - var manifest *Manifest - - bts, err := os.ReadFile(fp) + f, err := os.Open(fp) if err != nil { - return nil, "", fmt.Errorf("couldn't open file '%s'", fp) + return nil, "", err } + defer f.Close() - shaSum := sha256.Sum256(bts) - shaStr := hex.EncodeToString(shaSum[:]) + sha256sum := sha256.New() - if err := json.Unmarshal(bts, &manifest); err != nil { + var manifest Manifest + if err := json.NewDecoder(io.TeeReader(f, sha256sum)).Decode(&manifest); err != nil { return nil, "", err } - return manifest, shaStr, nil + return &manifest, hex.EncodeToString(sha256sum.Sum(nil)), nil } func GetModel(name string) (*Model, error) { @@ -716,7 +711,7 @@ func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}) // save (i.e. delete from the deleteMap) any files used in other manifests manifest, _, err := GetManifest(fmp) if err != nil { - return err + return fmt.Errorf("error reading manifest %s: %w", path, err) } for _, layer := range manifest.Layers { @@ -781,8 +776,7 @@ func PruneLayers() error { slog.Info(fmt.Sprintf("total blobs: %d", len(deleteMap))) - err = deleteUnusedLayers(nil, deleteMap) - if err != nil { + if err := deleteUnusedLayers(nil, deleteMap); err != nil { slog.Error(fmt.Sprintf("couldn't remove unused layers: %v", err)) return nil } @@ -877,26 +871,19 @@ func PushModel(ctx context.Context, name string, regOpts *registryOptions, fn fu func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn func(api.ProgressResponse)) error { mp := ParseModelPath(name) - var manifest *Manifest - var err error - var noprune string - // build deleteMap to prune unused layers deleteMap := make(map[string]struct{}) - - if !envconfig.NoPrune() { - manifest, _, err = GetManifest(mp) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return err + manifest, _, err := GetManifest(mp) + if errors.Is(err, os.ErrNotExist) { + // noop + } else if err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } else { + for _, l := range manifest.Layers { + deleteMap[l.Digest] = struct{}{} } - - if manifest != nil { - for _, l := range manifest.Layers { - deleteMap[l.Digest] = struct{}{} - } - if manifest.Config.Digest != "" { - deleteMap[manifest.Config.Digest] = struct{}{} - } + if manifest.Config.Digest != "" { + deleteMap[manifest.Config.Digest] = struct{}{} } } @@ -975,11 +962,9 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu return err } - if noprune == "" { - fn(api.ProgressResponse{Status: "removing any unused layers"}) - err = deleteUnusedLayers(nil, deleteMap) - if err != nil { - slog.Error(fmt.Sprintf("couldn't remove unused layers: %v", err)) + if !envconfig.NoPrune() && len(deleteMap) > 0 { + fn(api.ProgressResponse{Status: "removing unused layers"}) + if err := deleteUnusedLayers(nil, deleteMap); err != nil { fn(api.ProgressResponse{Status: fmt.Sprintf("couldn't remove unused layers: %v", err)}) } } @@ -1000,12 +985,12 @@ func pullModelManifest(ctx context.Context, mp ModelPath, regOpts *registryOptio } defer resp.Body.Close() - var m *Manifest + var m Manifest if err := json.NewDecoder(resp.Body).Decode(&m); err != nil { return nil, err } - return m, err + return &m, err } // GetSHA256Digest returns the SHA256 hash of a given buffer and returns it, and the size of buffer From 237dccba1edb41bb65ed1ffc6eafdd40dd6085e4 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Wed, 14 Aug 2024 16:36:07 -0700 Subject: [PATCH 038/174] skip invalid manifest files --- server/images.go | 35 +++++------------------------------ server/manifest.go | 2 +- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/server/images.go b/server/images.go index 798ed818..8b3a67cf 100644 --- a/server/images.go +++ b/server/images.go @@ -687,43 +687,18 @@ func CopyModel(src, dst model.Name) error { return err } -func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}) error { - fp, err := GetManifestPath() +func deleteUnusedLayers(deleteMap map[string]struct{}) error { + manifests, err := Manifests() if err != nil { return err } - walkFunc := func(path string, info os.FileInfo, _ error) error { - if info.IsDir() { - return nil - } - - dir, file := filepath.Split(path) - dir = strings.Trim(strings.TrimPrefix(dir, fp), string(os.PathSeparator)) - tag := strings.Join([]string{dir, file}, ":") - fmp := ParseModelPath(tag) - - // skip the manifest we're trying to delete - if skipModelPath != nil && skipModelPath.GetFullTagname() == fmp.GetFullTagname() { - return nil - } - - // save (i.e. delete from the deleteMap) any files used in other manifests - manifest, _, err := GetManifest(fmp) - if err != nil { - return fmt.Errorf("error reading manifest %s: %w", path, err) - } - + for _, manifest := range manifests { for _, layer := range manifest.Layers { delete(deleteMap, layer.Digest) } delete(deleteMap, manifest.Config.Digest) - return nil - } - - if err := filepath.Walk(fp, walkFunc); err != nil { - return err } // only delete the files which are still in the deleteMap @@ -776,7 +751,7 @@ func PruneLayers() error { slog.Info(fmt.Sprintf("total blobs: %d", len(deleteMap))) - if err := deleteUnusedLayers(nil, deleteMap); err != nil { + if err := deleteUnusedLayers(deleteMap); err != nil { slog.Error(fmt.Sprintf("couldn't remove unused layers: %v", err)) return nil } @@ -964,7 +939,7 @@ func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn fu if !envconfig.NoPrune() && len(deleteMap) > 0 { fn(api.ProgressResponse{Status: "removing unused layers"}) - if err := deleteUnusedLayers(nil, deleteMap); err != nil { + if err := deleteUnusedLayers(deleteMap); err != nil { fn(api.ProgressResponse{Status: fmt.Sprintf("couldn't remove unused layers: %v", err)}) } } diff --git a/server/manifest.go b/server/manifest.go index 6a5d7b88..0f19641d 100644 --- a/server/manifest.go +++ b/server/manifest.go @@ -150,7 +150,7 @@ func Manifests() (map[model.Name]*Manifest, error) { n := model.ParseNameFromFilepath(rel) if !n.IsValid() { - slog.Warn("bad manifest name", "path", rel, "error", err) + slog.Warn("bad manifest name", "path", rel) continue } From 3a75e74e34c976d596437c8aa14587ada562301e Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 15 Aug 2024 10:29:14 -0700 Subject: [PATCH 039/174] only skip invalid json manifests --- server/manifest.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/manifest.go b/server/manifest.go index 0f19641d..6b04753f 100644 --- a/server/manifest.go +++ b/server/manifest.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "errors" + "fmt" "io" "log/slog" "os" @@ -155,9 +156,11 @@ func Manifests() (map[model.Name]*Manifest, error) { } m, err := ParseNamedManifest(n) - if err != nil { + if syntax := &(json.SyntaxError{}); errors.As(err, &syntax) { slog.Warn("bad manifest", "name", n, "error", err) continue + } else if err != nil { + return nil, fmt.Errorf("%s: %w", n, err) } ms[n] = m From a84c05cf9140c2eb288a6c7b56bb1c592bbaacc7 Mon Sep 17 00:00:00 2001 From: eust-w Date: Fri, 16 Aug 2024 06:00:12 +0800 Subject: [PATCH 040/174] fix: Add tooltip to system tray icon - Updated setIcon method to include tooltip text for the system tray icon. - Added NIF_TIP flag and set the tooltip text using UTF16 encoding. Resolves: #6372 --- app/tray/wintray/tray.go | 8 +++++++- app/tray/wintray/w32api.go | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/tray/wintray/tray.go b/app/tray/wintray/tray.go index ccd087a1..6f827893 100644 --- a/app/tray/wintray/tray.go +++ b/app/tray/wintray/tray.go @@ -11,6 +11,7 @@ import ( "path/filepath" "sort" "sync" + "syscall" "unsafe" "golang.org/x/sys/windows" @@ -433,7 +434,12 @@ func (t *winTray) setIcon(src string) error { t.muNID.Lock() defer t.muNID.Unlock() t.nid.Icon = h - t.nid.Flags |= NIF_ICON + t.nid.Flags |= NIF_ICON | NIF_TIP + if toolTipUTF16, err := syscall.UTF16FromString(commontray.ToolTip); err == nil { + copy(t.nid.Tip[:], toolTipUTF16) + } else { + return err + } t.nid.Size = uint32(unsafe.Sizeof(*t.nid)) return t.nid.modify() diff --git a/app/tray/wintray/w32api.go b/app/tray/wintray/w32api.go index a1e0381d..7c7c0ac8 100644 --- a/app/tray/wintray/w32api.go +++ b/app/tray/wintray/w32api.go @@ -61,6 +61,7 @@ const ( MIIM_SUBMENU = 0x00000004 MIM_APPLYTOSUBMENUS = 0x80000000 NIF_ICON = 0x00000002 + NIF_TIP = 0x00000004 NIF_INFO = 0x00000010 NIF_MESSAGE = 0x00000001 SW_HIDE = 0 From bdc4308afb72d47ce63583427f810b02d569d58a Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Fri, 16 Aug 2024 11:43:19 +0800 Subject: [PATCH 041/174] fix: chmod new layer to 0o644 when creating it Signed-off-by: zwwhdls --- server/layer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/layer.go b/server/layer.go index c666bd10..0bdee72b 100644 --- a/server/layer.go +++ b/server/layer.go @@ -51,6 +51,9 @@ func NewLayer(r io.Reader, mediatype string) (Layer, error) { if err := os.Rename(temp.Name(), blob); err != nil { return Layer{}, err } + if err := os.Chmod(blob, 0o644); err != nil { + return Layer{}, err + } } return Layer{ From 0ad0e738cd7ed1266b3c210ad54dcd2b70142563 Mon Sep 17 00:00:00 2001 From: Richard Lyons Date: Sun, 18 Aug 2024 01:43:26 +0200 Subject: [PATCH 042/174] Override numParallel only if unset. --- server/sched.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/sched.go b/server/sched.go index 9947fd32..4d9c0296 100644 --- a/server/sched.go +++ b/server/sched.go @@ -734,7 +734,9 @@ func pickBestFullFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoL // If multiple Libraries are detected, pick the Library which loads the most layers for the model func pickBestPartialFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { - *numParallel = 1 + if *numParallel <= 0 { + *numParallel = 1 + } byLibrary := gpus.ByLibrary() if len(byLibrary) <= 1 { return gpus From 9352eeb752531decccc7c6b91a07bc3dd5efa67e Mon Sep 17 00:00:00 2001 From: Richard Lyons Date: Sun, 18 Aug 2024 02:55:01 +0200 Subject: [PATCH 043/174] Reset NumCtx. --- server/sched.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/sched.go b/server/sched.go index 4d9c0296..3fe6d7fc 100644 --- a/server/sched.go +++ b/server/sched.go @@ -736,6 +736,7 @@ func pickBestFullFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoL func pickBestPartialFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { if *numParallel <= 0 { *numParallel = 1 + req.opts.NumCtx = req.origNumCtx } byLibrary := gpus.ByLibrary() if len(byLibrary) <= 1 { From 885cf45087863aa2e064a05da99e8bd07d69970a Mon Sep 17 00:00:00 2001 From: Richard Lyons Date: Sun, 18 Aug 2024 03:07:16 +0200 Subject: [PATCH 044/174] Fix white space. --- server/sched.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/sched.go b/server/sched.go index 3fe6d7fc..9d8c4144 100644 --- a/server/sched.go +++ b/server/sched.go @@ -736,8 +736,8 @@ func pickBestFullFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoL func pickBestPartialFitByLibrary(req *LlmRequest, ggml *llm.GGML, gpus gpu.GpuInfoList, numParallel *int) gpu.GpuInfoList { if *numParallel <= 0 { *numParallel = 1 - req.opts.NumCtx = req.origNumCtx - } + req.opts.NumCtx = req.origNumCtx + } byLibrary := gpus.ByLibrary() if len(byLibrary) <= 1 { return gpus From 9fddef3731842bd8f40d217da6b84ab7ef5dfe97 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 19 Aug 2024 09:20:52 -0700 Subject: [PATCH 045/174] server: limit upload parts to 16 (#6411) --- server/upload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/upload.go b/server/upload.go index 2f115436..020e8955 100644 --- a/server/upload.go +++ b/server/upload.go @@ -45,7 +45,7 @@ type blobUpload struct { } const ( - numUploadParts = 64 + numUploadParts = 16 minUploadPartSize int64 = 100 * format.MegaByte maxUploadPartSize int64 = 1000 * format.MegaByte ) From 74d45f010276c2f2653f3ca8c4f76cb0552fb46e Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Mon, 8 Jul 2024 12:50:11 -0700 Subject: [PATCH 046/174] Refactor linux packaging This adjusts linux to follow a similar model to windows with a discrete archive (zip/tgz) to cary the primary executable, and dependent libraries. Runners are still carried as payloads inside the main binary Darwin retain the payload model where the go binary is fully self contained. --- .github/workflows/release.yaml | 1 - Dockerfile | 29 ++++++------ app/ollama.iss | 11 +---- envconfig/config.go | 4 +- gpu/amd_common.go | 2 +- gpu/amd_windows.go | 2 +- gpu/gpu.go | 50 ++++++++++++++------- gpu/gpu_linux.go | 2 +- llm/ext_server/CMakeLists.txt | 3 +- llm/generate/gen_common.sh | 17 ++++++- llm/generate/gen_linux.sh | 81 ++++++++++++++++------------------ llm/generate/gen_windows.ps1 | 43 +++++++++--------- llm/server.go | 12 +++-- scripts/build_linux.sh | 10 ++--- scripts/build_windows.ps1 | 12 ++--- scripts/install.sh | 31 ++++++++++--- 16 files changed, 171 insertions(+), 139 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5ae630c3..9287f6f7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -363,7 +363,6 @@ jobs: - run: | ./scripts/build_linux.sh ./scripts/build_docker.sh - mv dist/deps/* dist/ - uses: actions/upload-artifact@v4 with: name: dist-linux-amd64 diff --git a/Dockerfile b/Dockerfile index c8efdd8a..120ddc21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS +ENV GOARCH amd64 RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION-devel-rockylinux8 AS cuda-build-arm64 @@ -28,6 +29,7 @@ ENV PATH /opt/rh/gcc-toolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS +ENV GOARCH arm64 RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete AS rocm-build-amd64 @@ -40,15 +42,10 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG AMDGPU_TARGETS +ENV GOARCH amd64 RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh -RUN mkdir /tmp/scratch && \ - for dep in $(zcat /go/src/github.com/ollama/ollama/llm/build/linux/x86_64/rocm*/bin/deps.txt.gz) ; do \ - cp ${dep} /tmp/scratch/ || exit 1 ; \ - done && \ - (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd /tmp/scratch/ && tar xf - ) && \ - mkdir -p /go/src/github.com/ollama/ollama/dist/deps/ && \ - (cd /tmp/scratch/ && tar czvf /go/src/github.com/ollama/ollama/dist/deps/ollama-linux-amd64-rocm.tgz . ) - +RUN mkdir -p ../../dist/linux-amd64/ollama_libs && \ + (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64/ollama_libs && tar xf - ) FROM --platform=linux/amd64 centos:7 AS cpu-builder-amd64 ARG CMAKE_VERSION @@ -59,6 +56,7 @@ ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ ARG OLLAMA_CUSTOM_CPU_DEFS ARG CGO_CFLAGS +ENV GOARCH amd64 WORKDIR /go/src/github.com/ollama/ollama/llm/generate FROM --platform=linux/amd64 cpu-builder-amd64 AS static-build-amd64 @@ -79,6 +77,7 @@ ENV PATH /opt/rh/gcc-toolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ ARG OLLAMA_CUSTOM_CPU_DEFS ARG CGO_CFLAGS +ENV GOARCH arm64 WORKDIR /go/src/github.com/ollama/ollama/llm/generate FROM --platform=linux/arm64 cpu-builder-arm64 AS static-build-arm64 @@ -95,12 +94,13 @@ COPY . . COPY --from=static-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cpu_avx-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cpu_avx2-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=cuda-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/dist/deps/ ./dist/deps/ ARG GOFLAGS ARG CGO_CFLAGS -RUN go build -trimpath . +RUN go build -trimpath -o dist/linux-amd64/ollama . # Intermediate stage used for ./scripts/build_linux.sh FROM --platform=linux/arm64 cpu-build-arm64 AS build-arm64 @@ -109,23 +109,24 @@ ARG GOLANG_VERSION WORKDIR /go/src/github.com/ollama/ollama COPY . . COPY --from=static-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ARG GOFLAGS ARG CGO_CFLAGS -RUN go build -trimpath . +RUN go build -trimpath -o dist/linux-arm64/ollama . # Runtime stages FROM --platform=linux/amd64 ubuntu:22.04 as runtime-amd64 RUN apt-get update && apt-get install -y ca-certificates -COPY --from=build-amd64 /go/src/github.com/ollama/ollama/ollama /bin/ollama +COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/ollama /bin/ollama FROM --platform=linux/arm64 ubuntu:22.04 as runtime-arm64 RUN apt-get update && apt-get install -y ca-certificates -COPY --from=build-arm64 /go/src/github.com/ollama/ollama/ollama /bin/ollama +COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/ollama /bin/ollama # Radeon images are much larger so we keep it distinct from the CPU/CUDA image FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete as runtime-rocm RUN update-pciids -COPY --from=build-amd64 /go/src/github.com/ollama/ollama/ollama /bin/ollama +COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/ollama /bin/ollama EXPOSE 11434 ENV OLLAMA_HOST 0.0.0.0 diff --git a/app/ollama.iss b/app/ollama.iss index dc6178f7..e9cf48ec 100644 --- a/app/ollama.iss +++ b/app/ollama.iss @@ -91,16 +91,7 @@ Source: "..\ollama.exe"; DestDir: "{app}"; Flags: ignoreversion 64bit Source: "..\dist\windows-{#ARCH}\ollama_runners\*"; DestDir: "{app}\ollama_runners"; Flags: ignoreversion 64bit recursesubdirs Source: "..\dist\ollama_welcome.ps1"; DestDir: "{app}"; Flags: ignoreversion Source: ".\assets\app.ico"; DestDir: "{app}"; Flags: ignoreversion -#if DirExists("..\dist\windows-amd64\cuda") - Source: "..\dist\windows-amd64\cuda\*"; DestDir: "{app}\cuda\"; Flags: ignoreversion recursesubdirs -#endif -#if DirExists("..\dist\windows-amd64\oneapi") - Source: "..\dist\windows-amd64\oneapi\*"; DestDir: "{app}\oneapi\"; Flags: ignoreversion recursesubdirs -#endif -#if DirExists("..\dist\windows-amd64\rocm") - Source: "..\dist\windows-amd64\rocm\*"; DestDir: "{app}\rocm\"; Flags: ignoreversion recursesubdirs -#endif - +Source: "..\dist\windows-amd64\ollama_libs\*"; DestDir: "{app}\ollama_libs\"; Flags: ignoreversion recursesubdirs [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico" diff --git a/envconfig/config.go b/envconfig/config.go index b82b773d..7f0976c0 100644 --- a/envconfig/config.go +++ b/envconfig/config.go @@ -193,8 +193,8 @@ func RunnersDir() (p string) { for _, root := range []string{filepath.Dir(exe), cwd} { paths = append(paths, root, - filepath.Join(root, "windows-"+runtime.GOARCH), - filepath.Join(root, "dist", "windows-"+runtime.GOARCH), + filepath.Join(root, runtime.GOOS+"-"+runtime.GOARCH), + filepath.Join(root, "dist", runtime.GOOS+"-"+runtime.GOARCH), ) } diff --git a/gpu/amd_common.go b/gpu/amd_common.go index 2839cb7c..05747208 100644 --- a/gpu/amd_common.go +++ b/gpu/amd_common.go @@ -54,7 +54,7 @@ func commonAMDValidateLibDir() (string, error) { // Installer payload location if we're running the installed binary exe, err := os.Executable() if err == nil { - rocmTargetDir := filepath.Join(filepath.Dir(exe), "rocm") + rocmTargetDir := filepath.Join(filepath.Dir(exe), "ollama_libs") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ROCM next to ollama executable " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/amd_windows.go b/gpu/amd_windows.go index edabeb43..5d25a966 100644 --- a/gpu/amd_windows.go +++ b/gpu/amd_windows.go @@ -153,7 +153,7 @@ func AMDValidateLibDir() (string, error) { // Installer payload (if we're running from some other location) localAppData := os.Getenv("LOCALAPPDATA") appDir := filepath.Join(localAppData, "Programs", "Ollama") - rocmTargetDir := filepath.Join(appDir, "rocm") + rocmTargetDir := filepath.Join(appDir, "ollama_libs") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ollama installed ROCm at " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/gpu.go b/gpu/gpu.go index dc124a3e..d0ae0f34 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -229,11 +229,7 @@ func GetGPUInfo() GpuInfoList { return GpuInfoList{cpus[0].GpuInfo} } - // On windows we bundle the nvidia library one level above the runner dir - depPath := "" - if runtime.GOOS == "windows" && envconfig.RunnersDir() != "" { - depPath = filepath.Join(filepath.Dir(envconfig.RunnersDir()), "cuda") - } + depPath := GetDepDir() // Load ALL libraries cHandles = initCudaHandles() @@ -306,13 +302,6 @@ func GetGPUInfo() GpuInfoList { if envconfig.IntelGPU() { oHandles = initOneAPIHandles() if oHandles != nil && oHandles.oneapi != nil { - - // On windows we bundle the oneapi library one level above the runner dir - depPath = "" - if runtime.GOOS == "windows" && envconfig.RunnersDir() != "" { - depPath = filepath.Join(filepath.Dir(envconfig.RunnersDir()), "oneapi") - } - for d := range oHandles.oneapi.num_drivers { if oHandles.oneapi == nil { // shouldn't happen @@ -467,10 +456,12 @@ func GetGPUInfo() GpuInfoList { func FindGPULibs(baseLibName string, defaultPatterns []string) []string { // Multiple GPU libraries may exist, and some may not work, so keep trying until we exhaust them var ldPaths []string - var patterns []string gpuLibPaths := []string{} slog.Debug("Searching for GPU library", "name", baseLibName) + // Start with our bundled libraries + patterns := []string{filepath.Join(GetDepDir(), baseLibName)} + switch runtime.GOOS { case "windows": ldPaths = strings.Split(os.Getenv("PATH"), ";") @@ -479,13 +470,14 @@ func FindGPULibs(baseLibName string, defaultPatterns []string) []string { default: return gpuLibPaths } - // Start with whatever we find in the PATH/LD_LIBRARY_PATH + + // Then with whatever we find in the PATH/LD_LIBRARY_PATH for _, ldPath := range ldPaths { d, err := filepath.Abs(ldPath) if err != nil { continue } - patterns = append(patterns, filepath.Join(d, baseLibName+"*")) + patterns = append(patterns, filepath.Join(d, baseLibName)) } patterns = append(patterns, defaultPatterns...) slog.Debug("gpu library search", "globs", patterns) @@ -641,3 +633,31 @@ func (l GpuInfoList) GetVisibleDevicesEnv() (string, string) { return "", "" } } + +func GetDepDir() string { + // On Windows/linux we bundle the dependencies at the same level as the executable + appExe, err := os.Executable() + if err != nil { + slog.Warn("failed to lookup executable path", "error", err) + } + cwd, err := os.Getwd() + if err != nil { + slog.Warn("failed to lookup working directory", "error", err) + } + // Scan for any of our dependeices, and pick first match + for _, root := range []string{filepath.Dir(appExe), cwd} { + libDep := "ollama_libs" + if _, err := os.Stat(filepath.Join(root, libDep)); err == nil { + return filepath.Join(root, libDep) + } + // Developer mode, local build + if _, err := os.Stat(filepath.Join(root, runtime.GOOS+"-"+runtime.GOARCH, libDep)); err == nil { + return filepath.Join(root, runtime.GOOS+"-"+runtime.GOARCH, libDep) + } + if _, err := os.Stat(filepath.Join(root, "dist", runtime.GOOS+"-"+runtime.GOARCH, libDep)); err == nil { + return filepath.Join(root, "dist", runtime.GOOS+"-"+runtime.GOARCH, libDep) + } + } + slog.Warn("unable to locate gpu dependency libraries") + return "" +} diff --git a/gpu/gpu_linux.go b/gpu/gpu_linux.go index d6d2675c..d4d20bc4 100644 --- a/gpu/gpu_linux.go +++ b/gpu/gpu_linux.go @@ -47,7 +47,7 @@ var ( CudartMgmtName = "libcudart.so*" NvcudaMgmtName = "libcuda.so*" NvmlMgmtName = "" // not currently wired on linux - OneapiMgmtName = "libze_intel_gpu.so" + OneapiMgmtName = "libze_intel_gpu.so*" ) func GetCPUMem() (memInfo, error) { diff --git a/llm/ext_server/CMakeLists.txt b/llm/ext_server/CMakeLists.txt index bfc97c63..90fd0ef2 100644 --- a/llm/ext_server/CMakeLists.txt +++ b/llm/ext_server/CMakeLists.txt @@ -1,12 +1,13 @@ set(TARGET ollama_llama_server) option(LLAMA_SERVER_VERBOSE "Build verbose logging option for Server" ON) +set(LLAMA_SERVER_LDFLAGS $ENV{LLAMA_SERVER_LDFLAGS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(${TARGET} server.cpp utils.hpp json.hpp httplib.h) install(TARGETS ${TARGET} RUNTIME) target_compile_definitions(${TARGET} PRIVATE SERVER_VERBOSE=$ ) -target_link_libraries(${TARGET} PRIVATE ggml llama common llava ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${TARGET} PRIVATE ggml llama common llava ${CMAKE_THREAD_LIBS_INIT} ${LLAMA_SERVER_LDFLAGS}) if (WIN32) TARGET_LINK_LIBRARIES(${TARGET} PRIVATE ws2_32) endif() diff --git a/llm/generate/gen_common.sh b/llm/generate/gen_common.sh index da1b0688..f1541f2a 100644 --- a/llm/generate/gen_common.sh +++ b/llm/generate/gen_common.sh @@ -9,11 +9,14 @@ init_vars() { ARCH="arm64" ;; *) - ARCH=$(uname -m | sed -e "s/aarch64/arm64/g") + echo "GOARCH must be set" + echo "this script is meant to be run from within go generate" + exit 1 + ;; esac LLAMACPP_DIR=../llama.cpp - CMAKE_DEFS="" + CMAKE_DEFS="-DCMAKE_SKIP_RPATH=on" CMAKE_TARGETS="--target ollama_llama_server" if echo "${CGO_CFLAGS}" | grep -- '-g' >/dev/null; then CMAKE_DEFS="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=on -DLLAMA_GPROF=on -DLLAMA_SERVER_VERBOSE=on ${CMAKE_DEFS}" @@ -27,6 +30,7 @@ init_vars() { WHOLE_ARCHIVE="-Wl,-force_load" NO_WHOLE_ARCHIVE="" GCC_ARCH="-arch ${ARCH}" + DIST_BASE=../../dist/darwin-${GOARCH}/ ;; "Linux") LIB_EXT="so" @@ -35,6 +39,7 @@ init_vars() { # Cross compiling not supported on linux - Use docker GCC_ARCH="" + DIST_BASE=../../dist/linux-${GOARCH}/ ;; *) ;; @@ -105,6 +110,14 @@ compress() { echo "Finished compression" } +install() { + echo "Installing libraries to bin dir ${BUILD_DIR}/bin/" + for lib in $(find ${BUILD_DIR} -name \*.${LIB_EXT}); do + rm -f "${BUILD_DIR}/bin/$(basename ${lib})" + cp -af "${lib}" "${BUILD_DIR}/bin/" + done +} + # Keep the local tree clean after we're done with the build cleanup() { (cd ${LLAMACPP_DIR}/ && git checkout CMakeLists.txt) diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index db2c6c30..70fc0313 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -51,7 +51,7 @@ if [ -z "${CUDACXX}" ]; then export CUDACXX=$(command -v nvcc) fi fi -COMMON_CMAKE_DEFS="-DBUILD_SHARED_LIBS=off -DCMAKE_POSITION_INDEPENDENT_CODE=on -DGGML_NATIVE=off -DGGML_AVX=on -DGGML_AVX2=off -DGGML_AVX512=off -DGGML_FMA=off -DGGML_F16C=off -DGGML_OPENMP=off" +COMMON_CMAKE_DEFS="-DCMAKE_SKIP_RPATH=on -DBUILD_SHARED_LIBS=on -DCMAKE_POSITION_INDEPENDENT_CODE=on -DGGML_NATIVE=off -DGGML_AVX=on -DGGML_AVX2=off -DGGML_AVX512=off -DGGML_FMA=off -DGGML_F16C=off -DGGML_OPENMP=off" source $(dirname $0)/gen_common.sh init_vars git_module_setup @@ -77,10 +77,11 @@ if [ -z "${OLLAMA_SKIP_CPU_GENERATE}" ]; then if [ -n "${OLLAMA_CUSTOM_CPU_DEFS}" ]; then init_vars echo "OLLAMA_CUSTOM_CPU_DEFS=\"${OLLAMA_CUSTOM_CPU_DEFS}\"" - CMAKE_DEFS="${OLLAMA_CUSTOM_CPU_DEFS} -DBUILD_SHARED_LIBS=off -DCMAKE_POSITION_INDEPENDENT_CODE=on ${CMAKE_DEFS}" + CMAKE_DEFS="${OLLAMA_CUSTOM_CPU_DEFS} -DBUILD_SHARED_LIBS=on -DCMAKE_POSITION_INDEPENDENT_CODE=on ${CMAKE_DEFS}" BUILD_DIR="../build/linux/${ARCH}/cpu" echo "Building custom CPU" build + install compress else # Darwin Rosetta x86 emulation does NOT support AVX, AVX2, AVX512 @@ -93,7 +94,7 @@ if [ -z "${OLLAMA_SKIP_CPU_GENERATE}" ]; then # -DGGML_AVX512_VBMI -- 2018 Intel Cannon Lake # -DGGML_AVX512_VNNI -- 2021 Intel Alder Lake - COMMON_CPU_DEFS="-DBUILD_SHARED_LIBS=off -DCMAKE_POSITION_INDEPENDENT_CODE=on -DGGML_NATIVE=off -DGGML_OPENMP=off" + COMMON_CPU_DEFS="-DBUILD_SHARED_LIBS=on -DCMAKE_POSITION_INDEPENDENT_CODE=on -DGGML_NATIVE=off -DGGML_OPENMP=off" if [ -z "${OLLAMA_CPU_TARGET}" -o "${OLLAMA_CPU_TARGET}" = "cpu" ]; then # # CPU first for the default library, set up as lowest common denominator for maximum compatibility (including Rosetta) @@ -103,6 +104,7 @@ if [ -z "${OLLAMA_SKIP_CPU_GENERATE}" ]; then BUILD_DIR="../build/linux/${ARCH}/cpu" echo "Building LCD CPU" build + install compress fi @@ -120,6 +122,7 @@ if [ -z "${OLLAMA_SKIP_CPU_GENERATE}" ]; then BUILD_DIR="../build/linux/${ARCH}/cpu_avx" echo "Building AVX CPU" build + install compress fi @@ -133,6 +136,7 @@ if [ -z "${OLLAMA_SKIP_CPU_GENERATE}" ]; then BUILD_DIR="../build/linux/${ARCH}/cpu_avx2" echo "Building AVX2 CPU" build + install compress fi fi @@ -178,29 +182,18 @@ if [ -z "${OLLAMA_SKIP_CUDA_GENERATE}" -a -d "${CUDA_LIB_DIR}" ]; then CMAKE_CUDA_DEFS="-DGGML_CUDA=on -DCMAKE_CUDA_ARCHITECTURES=${CMAKE_CUDA_ARCHITECTURES} ${OLLAMA_CUSTOM_CUDA_DEFS}" echo "Building custom CUDA GPU" else - CMAKE_CUDA_DEFS="-DGGML_CUDA=on -DCMAKE_CUDA_FLAGS=-t8 -DCMAKE_CUDA_ARCHITECTURES=${CMAKE_CUDA_ARCHITECTURES}" + CMAKE_CUDA_DEFS="-DGGML_CUDA=on -DCMAKE_CUDA_ARCHITECTURES=${CMAKE_CUDA_ARCHITECTURES}" fi - CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} ${ARM64_DEFS} ${CMAKE_CUDA_DEFS}" + export CUDAFLAGS="-t8" + CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} ${ARM64_DEFS} ${CMAKE_CUDA_DEFS} -DGGML_STATIC=off" BUILD_DIR="../build/linux/${ARCH}/cuda${CUDA_VARIANT}" - EXTRA_LIBS="-L${CUDA_LIB_DIR} -lcudart -lcublas -lcublasLt -lcuda" + export LLAMA_SERVER_LDFLAGS="-L${CUDA_LIB_DIR} -lcudart -lcublas -lcublasLt -lcuda" + CUDA_DIST_DIR="${DIST_BASE}/ollama_libs" build - - # Carry the CUDA libs as payloads to help reduce dependency burden on users - # - # TODO - in the future we may shift to packaging these separately and conditionally - # downloading them in the install script. - DEPS="$(ldd ${BUILD_DIR}/bin/ollama_llama_server )" - for lib in libcudart.so libcublas.so libcublasLt.so ; do - DEP=$(echo "${DEPS}" | grep ${lib} | cut -f1 -d' ' | xargs || true) - if [ -n "${DEP}" -a -e "${CUDA_LIB_DIR}/${DEP}" ]; then - cp "${CUDA_LIB_DIR}/${DEP}" "${BUILD_DIR}/bin/" - elif [ -e "${CUDA_LIB_DIR}/${lib}.${CUDA_MAJOR}" ]; then - cp "${CUDA_LIB_DIR}/${lib}.${CUDA_MAJOR}" "${BUILD_DIR}/bin/" - elif [ -e "${CUDART_LIB_DIR}/${lib}" ]; then - cp -d ${CUDART_LIB_DIR}/${lib}* "${BUILD_DIR}/bin/" - else - cp -d "${CUDA_LIB_DIR}/${lib}*" "${BUILD_DIR}/bin/" - fi + install + mkdir -p "${CUDA_DIST_DIR}" + for lib in ${CUDA_LIB_DIR}/libcudart.so* ${CUDA_LIB_DIR}/libcublas.so* ${CUDA_LIB_DIR}/libcublasLt.so* ; do + cp -a "${lib}" "${CUDA_DIST_DIR}" done compress @@ -218,21 +211,24 @@ if [ -z "${OLLAMA_SKIP_ONEAPI_GENERATE}" -a -d "${ONEAPI_ROOT}" ]; then CC=icx CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL=ON -DGGML_SYCL_F16=OFF" BUILD_DIR="../build/linux/${ARCH}/oneapi" - EXTRA_LIBS="-fsycl -Wl,-rpath,${ONEAPI_ROOT}/compiler/latest/lib,-rpath,${ONEAPI_ROOT}/mkl/latest/lib,-rpath,${ONEAPI_ROOT}/tbb/latest/lib,-rpath,${ONEAPI_ROOT}/compiler/latest/opt/oclfpga/linux64/lib -lOpenCL -lmkl_core -lmkl_sycl_blas -lmkl_intel_ilp64 -lmkl_tbb_thread -ltbb" + ONEAPI_DIST_DIR="${DIST_BASE}/ollama_libs" + export LLAMA_SERVER_LDFLAGS="-fsycl -lOpenCL -lmkl_core -lmkl_sycl_blas -lmkl_intel_ilp64 -lmkl_tbb_thread -ltbb" DEBUG_FLAGS="" # icx compiles with -O0 if we pass -g, so we must remove it build # copy oneAPI dependencies + mkdir -p "${ONEAPI_DIST_DIR}" for dep in $(ldd "${BUILD_DIR}/bin/ollama_llama_server" | grep "=>" | cut -f2 -d= | cut -f2 -d' ' | grep -e sycl -e mkl -e tbb); do - cp "${dep}" "${BUILD_DIR}/bin/" + cp -a "${dep}" "${ONEAPI_DIST_DIR}" done - cp "${ONEAPI_ROOT}/compiler/latest/lib/libOpenCL.so" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libimf.so" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libintlc.so.5" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libirng.so" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libpi_level_zero.so" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libsvml.so" "${BUILD_DIR}/bin/" - cp "${ONEAPI_ROOT}/compiler/latest/lib/libur_loader.so.0" "${BUILD_DIR}/bin/" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libOpenCL.so" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libimf.so" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libintlc.so.5" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libirng.so" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libpi_level_zero.so" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libsvml.so" "${ONEAPI_DIST_DIR}" + cp "${ONEAPI_ROOT}/compiler/latest/lib/libur_loader.so.0" "${ONEAPI_DIST_DIR}" + install compress fi @@ -262,21 +258,18 @@ if [ -z "${OLLAMA_SKIP_ROCM_GENERATE}" -a -d "${ROCM_PATH}" ]; then echo "Building custom ROCM GPU" fi BUILD_DIR="../build/linux/${ARCH}/rocm${ROCM_VARIANT}" - EXTRA_LIBS="-L${ROCM_PATH}/lib -L/opt/amdgpu/lib/x86_64-linux-gnu/ -Wl,-rpath,\$ORIGIN/../../rocm/ -lhipblas -lrocblas -lamdhip64 -lrocsolver -lamd_comgr -lhsa-runtime64 -lrocsparse -ldrm -ldrm_amdgpu" + ROCM_DIST_DIR="${DIST_BASE}/ollama_libs" + # TODO figure out how to disable runpath (rpath) + # export CMAKE_HIP_FLAGS="-fno-rtlib-add-rpath" # doesn't work + export LLAMA_SERVER_LDFLAGS="-L${ROCM_PATH}/lib -L/opt/amdgpu/lib/x86_64-linux-gnu/ -lhipblas -lrocblas -lamdhip64 -lrocsolver -lamd_comgr -lhsa-runtime64 -lrocsparse -ldrm -ldrm_amdgpu" build - # Record the ROCM dependencies - rm -f "${BUILD_DIR}/bin/deps.txt" - touch "${BUILD_DIR}/bin/deps.txt" - for dep in $(ldd "${BUILD_DIR}/bin/ollama_llama_server" | grep "=>" | cut -f2 -d= | cut -f2 -d' ' | grep -e rocm -e amdgpu -e libtinfo ); do - echo "${dep}" >> "${BUILD_DIR}/bin/deps.txt" + # copy the ROCM dependencies + mkdir -p "${ROCM_DIST_DIR}" + for dep in $(ldd "${BUILD_DIR}/bin/ollama_llama_server" | grep "=>" | cut -f2 -d= | cut -f2 -d' ' | grep -v "${ARCH}/rocm${ROCM_VARIANT}" | grep -e rocm -e amdgpu -e libtinfo ); do + cp -a "${dep}"* "${ROCM_DIST_DIR}" done - # bomb out if for some reason we didn't get a few deps - if [ $(cat "${BUILD_DIR}/bin/deps.txt" | wc -l ) -lt 8 ] ; then - cat "${BUILD_DIR}/bin/deps.txt" - echo "ERROR: deps file short" - exit 1 - fi + install compress fi diff --git a/llm/generate/gen_windows.ps1 b/llm/generate/gen_windows.ps1 index d8bce92d..1f8c96d8 100644 --- a/llm/generate/gen_windows.ps1 +++ b/llm/generate/gen_windows.ps1 @@ -286,12 +286,11 @@ function build_cuda() { sign install - rm -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" -ea 0 > $null - write-host "copying CUDA dependencies to ${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" - cp "${script:CUDA_LIB_DIR}\cudart64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" - cp "${script:CUDA_LIB_DIR}\cublas64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" - cp "${script:CUDA_LIB_DIR}\cublasLt64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\cuda\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" -ea 0 > $null + write-host "copying CUDA dependencies to ${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${script:CUDA_LIB_DIR}\cudart64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${script:CUDA_LIB_DIR}\cublas64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${script:CUDA_LIB_DIR}\cublasLt64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" } else { write-host "Skipping CUDA generation step" } @@ -325,18 +324,17 @@ function build_oneapi() { sign install - rm -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" -ea 0 > $null - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libirngmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libmmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_level_zero.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_unified_runtime.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_win_proxy_loader.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\svml_dispmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\sycl7.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_core.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_sycl_blas.4.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_tbb_thread.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\oneapi\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" -ea 0 > $null + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libirngmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libmmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_level_zero.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_unified_runtime.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_win_proxy_loader.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\svml_dispmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\sycl7.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_core.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_sycl_blas.4.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_tbb_thread.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" } else { Write-Host "Skipping oneAPI generation step" } @@ -386,12 +384,11 @@ function build_rocm() { sign install - rm -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\" - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\rocblas\library\" -ea 0 > $null - cp "${env:HIP_PATH}\bin\hipblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\" - cp "${env:HIP_PATH}\bin\rocblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\rocblas\library\" -ea 0 > $null + cp "${env:HIP_PATH}\bin\hipblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + cp "${env:HIP_PATH}\bin\rocblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" # amdhip64.dll dependency comes from the driver and must be installed on the host to use AMD GPUs - cp "${env:HIP_PATH}\bin\rocblas\library\*" "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\rocblas\library\" + cp "${env:HIP_PATH}\bin\rocblas\library\*" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\rocblas\library\" } else { write-host "Skipping ROCm generation step" } diff --git a/llm/server.go b/llm/server.go index d2b8db9b..9347a458 100644 --- a/llm/server.go +++ b/llm/server.go @@ -306,20 +306,18 @@ func NewLlamaServer(gpus gpu.GpuInfoList, model string, ggml *GGML, adapters, pr if runtime.GOOS == "windows" { pathEnv = "PATH" } - // prepend the server directory to LD_LIBRARY_PATH/PATH and the parent dir for common dependencies - libraryPaths := []string{dir, filepath.Dir(dir)} + // Start with the server directory for the LD_LIBRARY_PATH/PATH + libraryPaths := []string{dir} if libraryPath, ok := os.LookupEnv(pathEnv); ok { - // Append our runner directory to the path - // This will favor system libraries over our bundled library dependencies + // favor our bundled library dependencies over system libraries libraryPaths = append(libraryPaths, filepath.SplitList(libraryPath)...) } // Note: we always put the dependency path first - // since this was the exact version we verified for AMD GPUs - // and we favor what the user had in their path + // since this was the exact version we compiled/linked against if gpus[0].DependencyPath != "" { - // TODO refine for multi-gpu support + // assume gpus from the same library have the same dependency path libraryPaths = append([]string{gpus[0].DependencyPath}, libraryPaths...) } diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh index 27c4ff1f..4ea51229 100755 --- a/scripts/build_linux.sh +++ b/scripts/build_linux.sh @@ -21,11 +21,9 @@ for TARGETARCH in ${BUILD_ARCH}; do -t builder:$TARGETARCH \ . docker create --platform linux/$TARGETARCH --name builder-$TARGETARCH builder:$TARGETARCH - docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/ollama ./dist/ollama-linux-$TARGETARCH - - if [ "$TARGETARCH" = "amd64" ]; then - docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/dist/deps/ ./dist/ - fi - + docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/dist/linux-$TARGETARCH ./dist docker rm builder-$TARGETARCH + echo "Compressing final linux bundle..." + rm -f ./dist/ollama-linux-$TARGETARCH.tgz + (cd dist/linux-$TARGETARCH && tar cf - . | gzip --best > ../ollama-linux-$TARGETARCH.tgz ) done diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1 index edc73759..e8d851f4 100644 --- a/scripts/build_windows.ps1 +++ b/scripts/build_windows.ps1 @@ -103,22 +103,22 @@ function buildApp() { function gatherDependencies() { write-host "Gathering runtime dependencies" cd "${script:SRC_DIR}" - md "${script:DEPS_DIR}\ollama_runners" -ea 0 > $null + md "${script:DEPS_DIR}\ollama_libs" -ea 0 > $null # TODO - this varies based on host build system and MSVC version - drive from dumpbin output # currently works for Win11 + MSVC 2019 + Cuda V11 - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DEPS_DIR}\ollama_runners\" - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DEPS_DIR}\ollama_runners\" - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140_1.dll" "${script:DEPS_DIR}\ollama_runners\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DEPS_DIR}\ollama_libs\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DEPS_DIR}\ollama_libs\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140_1.dll" "${script:DEPS_DIR}\ollama_libs\" foreach ($part in $("runtime", "stdio", "filesystem", "math", "convert", "heap", "string", "time", "locale", "environment")) { - cp "$env:VCToolsRedistDir\..\..\..\Tools\Llvm\x64\bin\api-ms-win-crt-${part}*.dll" "${script:DEPS_DIR}\ollama_runners\" + cp "$env:VCToolsRedistDir\..\..\..\Tools\Llvm\x64\bin\api-ms-win-crt-${part}*.dll" "${script:DEPS_DIR}\ollama_libs\" } cp "${script:SRC_DIR}\app\ollama_welcome.ps1" "${script:SRC_DIR}\dist\" if ("${env:KEY_CONTAINER}") { write-host "about to sign" - foreach ($file in (get-childitem "${script:DEPS_DIR}\cuda\cu*.dll") + @("${script:SRC_DIR}\dist\ollama_welcome.ps1")){ + foreach ($file in (get-childitem "${script:DEPS_DIR}\ollama_libs\cu*.dll") + @("${script:SRC_DIR}\dist\ollama_welcome.ps1")){ write-host "signing $file" & "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" ` /csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} $file diff --git a/scripts/install.sh b/scripts/install.sh index 03af5a69..f0439b00 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -63,16 +63,32 @@ if [ -n "$NEEDS" ]; then exit 1 fi -status "Downloading ollama..." -curl --fail --show-error --location --progress-bar -o $TEMP_DIR/ollama "https://ollama.com/download/ollama-linux-${ARCH}${VER_PARAM}" - for BINDIR in /usr/local/bin /usr/bin /bin; do echo $PATH | grep -q $BINDIR && break || continue done +OLLAMA_INSTALL_DIR=${OLLAMA_INSTALL_DIR:-${BINDIR}} -status "Installing ollama to $BINDIR..." +status "Installing ollama to $OLLAMA_INSTALL_DIR" $SUDO install -o0 -g0 -m755 -d $BINDIR -$SUDO install -o0 -g0 -m755 $TEMP_DIR/ollama $BINDIR/ollama +$SUDO install -o0 -g0 -m755 -d "$OLLAMA_INSTALL_DIR" +if curl -I --silent --fail --location "https://ollama.com/download/ollama-linux-${ARCH}.tgz${VER_PARAM}" >/dev/null ; then + status "Downloading Linux ${ARCH} bundle" + curl --fail --show-error --location --progress-bar \ + "https://ollama.com/download/ollama-linux-${ARCH}.tgz${VER_PARAM}" | \ + $SUDO tar -xzf - -C "$OLLAMA_INSTALL_DIR" + BUNDLE=1 +else + status "Downloading Linux ${ARCH} CLI" + curl --fail --show-error --location --progress-bar -o "$TEMP_DIR/ollama"\ + "https://ollama.com/download/ollama-linux-${ARCH}${VER_PARAM}" + $SUDO install -o0 -g0 -m755 $TEMP_DIR/ollama $OLLAMA_INSTALL_DIR/ollama + BUNDLE=0 +fi + +if [ "$OLLAMA_INSTALL_DIR/ollama" != "$BINDIR/ollama" ] ; then + status "Making ollama accessible in the PATH in $BINDIR" + $SUDO ln -sf "$OLLAMA_INSTALL_DIR/ollama" "$BINDIR/ollama" +fi install_success() { status 'The Ollama API is now available at 127.0.0.1:11434.' @@ -178,6 +194,11 @@ if ! check_gpu lspci nvidia && ! check_gpu lshw nvidia && ! check_gpu lspci amdg fi if check_gpu lspci amdgpu || check_gpu lshw amdgpu; then + if [ $BUNDLE -ne 0 ]; then + install_success + status "AMD GPU ready." + exit 0 + fi # Look for pre-existing ROCm v6 before downloading the dependencies for search in "${HIP_PATH:-''}" "${ROCM_PATH:-''}" "/opt/rocm" "/usr/lib64"; do if [ -n "${search}" ] && [ -e "${search}/libhipblas.so.2" -o -e "${search}/lib/libhipblas.so.2" ]; then From c7bcb0031965e33531358639620a11516d101b54 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 9 Aug 2024 07:21:40 -0700 Subject: [PATCH 047/174] Wire up ccache and pigz in the docker based build This should help speed things up a little --- Dockerfile | 37 ++++++++++++++++++++++++++----------- llm/generate/gen_common.sh | 15 +++++++++------ llm/generate/gen_darwin.sh | 2 ++ llm/generate/gen_linux.sh | 2 ++ scripts/build_linux.sh | 3 ++- scripts/rh_linux_deps.sh | 14 ++++++++++++-- 6 files changed, 53 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index 120ddc21..8eb90057 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,8 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ENV GOARCH amd64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION-devel-rockylinux8 AS cuda-build-arm64 ARG CMAKE_VERSION @@ -30,7 +31,12 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ENV GOARCH arm64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CMAKE_CUDA_ARCHITECTURES="${CUDA_V11_ARCHITECTURES}" \ + CUDA_VARIANT="_v11" \ + bash gen_linux.sh FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete AS rocm-build-amd64 ARG CMAKE_VERSION @@ -43,7 +49,8 @@ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG AMDGPU_TARGETS ENV GOARCH amd64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh RUN mkdir -p ../../dist/linux-amd64/ollama_libs && \ (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64/ollama_libs && tar xf - ) @@ -60,13 +67,17 @@ ENV GOARCH amd64 WORKDIR /go/src/github.com/ollama/ollama/llm/generate FROM --platform=linux/amd64 cpu-builder-amd64 AS static-build-amd64 -RUN OLLAMA_CPU_TARGET="static" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_CPU_TARGET="static" bash gen_linux.sh FROM --platform=linux/amd64 cpu-builder-amd64 AS cpu-build-amd64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu" bash gen_linux.sh FROM --platform=linux/amd64 cpu-builder-amd64 AS cpu_avx-build-amd64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu_avx" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu_avx" bash gen_linux.sh FROM --platform=linux/amd64 cpu-builder-amd64 AS cpu_avx2-build-amd64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu_avx2" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu_avx2" bash gen_linux.sh FROM --platform=linux/arm64 rockylinux:8 AS cpu-builder-arm64 ARG CMAKE_VERSION @@ -81,9 +92,11 @@ ENV GOARCH arm64 WORKDIR /go/src/github.com/ollama/ollama/llm/generate FROM --platform=linux/arm64 cpu-builder-arm64 AS static-build-arm64 -RUN OLLAMA_CPU_TARGET="static" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_CPU_TARGET="static" bash gen_linux.sh FROM --platform=linux/arm64 cpu-builder-arm64 AS cpu-build-arm64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu" sh gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_CPU_TARGET="cpu" bash gen_linux.sh # Intermediate stage used for ./scripts/build_linux.sh @@ -100,7 +113,8 @@ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ARG GOFLAGS ARG CGO_CFLAGS -RUN go build -trimpath -o dist/linux-amd64/ollama . +RUN --mount=type=cache,target=/root/.ccache \ + go build -trimpath -o dist/linux-amd64/ollama . # Intermediate stage used for ./scripts/build_linux.sh FROM --platform=linux/arm64 cpu-build-arm64 AS build-arm64 @@ -113,7 +127,8 @@ COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ARG GOFLAGS ARG CGO_CFLAGS -RUN go build -trimpath -o dist/linux-arm64/ollama . +RUN --mount=type=cache,target=/root/.ccache \ + go build -trimpath -o dist/linux-arm64/ollama . # Runtime stages FROM --platform=linux/amd64 ubuntu:22.04 as runtime-amd64 diff --git a/llm/generate/gen_common.sh b/llm/generate/gen_common.sh index f1541f2a..40115936 100644 --- a/llm/generate/gen_common.sh +++ b/llm/generate/gen_common.sh @@ -47,6 +47,7 @@ init_vars() { if [ -z "${CMAKE_CUDA_ARCHITECTURES}" ] ; then CMAKE_CUDA_ARCHITECTURES="50;52;61;70;75;80" fi + GZIP=$(which pigz 2>/dev/null || echo "gzip") } git_module_setup() { @@ -90,21 +91,23 @@ build() { compress() { echo "Compressing payloads to reduce overall binary size..." - pids="" rm -rf ${BUILD_DIR}/bin/*.gz for f in ${BUILD_DIR}/bin/* ; do - gzip -n --best -f ${f} & - pids+=" $!" + ${GZIP} -n --best -f ${f} & + compress_pids+=" $!" done # check for lib directory if [ -d ${BUILD_DIR}/lib ]; then for f in ${BUILD_DIR}/lib/* ; do - gzip -n --best -f ${f} & - pids+=" $!" + ${GZIP} -n --best -f ${f} & + compress_pids+=" $!" done fi echo - for pid in ${pids}; do +} + +wait_for_compress() { + for pid in ${compress_pids}; do wait $pid done echo "Finished compression" diff --git a/llm/generate/gen_darwin.sh b/llm/generate/gen_darwin.sh index 6c0b62cb..f22c0f8e 100755 --- a/llm/generate/gen_darwin.sh +++ b/llm/generate/gen_darwin.sh @@ -6,6 +6,7 @@ set -ex set -o pipefail +compress_pids="" echo "Starting darwin generate script" source $(dirname $0)/gen_common.sh init_vars @@ -98,4 +99,5 @@ case "${GOARCH}" in esac cleanup +wait_for_compress echo "go generate completed. LLM runners: $(cd ${BUILD_DIR}/..; echo *)" diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index 70fc0313..1365d07d 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -13,6 +13,7 @@ set -ex set -o pipefail +compress_pids="" # See https://llvm.org/docs/AMDGPUUsage.html#processors for reference amdGPUs() { @@ -274,4 +275,5 @@ if [ -z "${OLLAMA_SKIP_ROCM_GENERATE}" -a -d "${ROCM_PATH}" ]; then fi cleanup +wait_for_compress echo "go generate completed. LLM runners: $(cd ${BUILD_DIR}/..; echo *)" diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh index 4ea51229..ebb60c5a 100755 --- a/scripts/build_linux.sh +++ b/scripts/build_linux.sh @@ -4,6 +4,7 @@ set -eu export VERSION=${VERSION:-$(git describe --tags --first-parent --abbrev=7 --long --dirty --always | sed -e "s/^v//g")} export GOFLAGS="'-ldflags=-w -s \"-X=github.com/ollama/ollama/version.Version=$VERSION\" \"-X=github.com/ollama/ollama/server.mode=release\"'" +GZIP=$(which pigz 2>/dev/null || echo "gzip") BUILD_ARCH=${BUILD_ARCH:-"amd64 arm64"} export AMDGPU_TARGETS=${AMDGPU_TARGETS:=""} @@ -25,5 +26,5 @@ for TARGETARCH in ${BUILD_ARCH}; do docker rm builder-$TARGETARCH echo "Compressing final linux bundle..." rm -f ./dist/ollama-linux-$TARGETARCH.tgz - (cd dist/linux-$TARGETARCH && tar cf - . | gzip --best > ../ollama-linux-$TARGETARCH.tgz ) + (cd dist/linux-$TARGETARCH && tar cf - . | ${GZIP} --best > ../ollama-linux-$TARGETARCH.tgz ) done diff --git a/scripts/rh_linux_deps.sh b/scripts/rh_linux_deps.sh index 81648d68..b4c9afd6 100644 --- a/scripts/rh_linux_deps.sh +++ b/scripts/rh_linux_deps.sh @@ -3,6 +3,7 @@ # Script for common Dockerfile dependency installation in redhat linux based images set -ex +set -o pipefail MACHINE=$(uname -m) if grep -i "centos" /etc/system-release >/dev/null; then @@ -29,7 +30,7 @@ if grep -i "centos" /etc/system-release >/dev/null; then dnf install -y rh-git227-git ln -s /opt/rh/rh-git227/root/usr/bin/git /usr/local/bin/git fi - dnf install -y devtoolset-10-gcc devtoolset-10-gcc-c++ + dnf install -y devtoolset-10-gcc devtoolset-10-gcc-c++ pigz elif grep -i "rocky" /etc/system-release >/dev/null; then # Temporary workaround until rocky 8 AppStream ships GCC 10.4 (10.3 is incompatible with NVCC) cat << EOF > /etc/yum.repos.d/Rocky-Vault.repo @@ -43,12 +44,21 @@ gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial EOF dnf install -y git \ gcc-toolset-10-gcc-10.2.1-8.2.el8 \ - gcc-toolset-10-gcc-c++-10.2.1-8.2.el8 + gcc-toolset-10-gcc-c++-10.2.1-8.2.el8 \ + pigz else echo "ERROR Unexpected distro" exit 1 fi +if [ "${MACHINE}" = "x86_64" ] ; then + curl -s -L https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-linux-x86_64.tar.xz | tar -Jx -C /tmp --strip-components 1 && \ + mv /tmp/ccache /usr/local/bin/ +else + yum -y install epel-release + yum install -y ccache +fi + if [ -n "${CMAKE_VERSION}" ]; then curl -s -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz | tar -zx -C /usr --strip-components 1 fi From d470ebe78bc76c098bc378f98f08f7094063ab4d Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 30 May 2024 21:54:07 -0700 Subject: [PATCH 048/174] Add Jetson cuda variants for arm This adds new variants for arm64 specific to Jetson platforms --- Dockerfile | 48 +++++++++++++++++++++++++++++++++++---- gpu/gpu.go | 44 +++++++++++++++++++++++++++++++++-- gpu/gpu_darwin.go | 4 ++-- gpu/types.go | 6 ++--- llm/generate/gen_linux.sh | 5 ++-- llm/payload.go | 4 ++-- scripts/build_linux.sh | 1 + 7 files changed, 96 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8eb90057..79b2a696 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,9 @@ ARG CMAKE_VERSION=3.22.1 # this CUDA_VERSION corresponds with the one specified in docs/gpu.md ARG CUDA_VERSION=11.3.1 ARG ROCM_VERSION=6.1.2 +ARG JETPACK_6=r36.2.0 +ARG JETPACK_5=r35.4.1 +ARG JETPACK_4=r32.7.1 # Copy the minimal context we need to run the generate scripts FROM scratch AS llm-code @@ -22,7 +25,7 @@ ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh -FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION-devel-rockylinux8 AS cuda-build-arm64 +FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION-devel-rockylinux8 AS cuda-build-server-arm64 ARG CMAKE_VERSION COPY ./scripts/rh_linux_deps.sh / RUN CMAKE_VERSION=${CMAKE_VERSION} sh /rh_linux_deps.sh @@ -31,11 +34,40 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ENV GOARCH arm64 +RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh + +FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_6} AS cuda-build-jetpack6-arm64 +ARG CMAKE_VERSION +RUN apt-get update && apt-get install -y git curl && \ + curl -s -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz | tar -zx -C /usr --strip-components 1 +COPY --from=llm-code / /go/src/github.com/ollama/ollama/ +WORKDIR /go/src/github.com/ollama/ollama/llm/generate +ARG CGO_CFLAGS +ENV GOARCH arm64 +ENV LIBRARY_PATH /usr/local/cuda/lib64/stubs RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ - CMAKE_CUDA_ARCHITECTURES="${CUDA_V11_ARCHITECTURES}" \ - CUDA_VARIANT="_v11" \ + CUDA_VARIANT="_jetpack6" \ + CUDA_DIST_DIR="/go/src/github.com/ollama/ollama/dist/linux-arm64/ollama_libs/cuda_jetpack6" \ + CMAKE_CUDA_ARCHITECTURES="87" \ + bash gen_linux.sh + +FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_5} AS cuda-build-jetpack5-arm64 +ARG CMAKE_VERSION +RUN apt-get update && apt-get install -y git curl && \ + curl -s -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz | tar -zx -C /usr --strip-components 1 +COPY --from=llm-code / /go/src/github.com/ollama/ollama/ +WORKDIR /go/src/github.com/ollama/ollama/llm/generate +ARG CGO_CFLAGS +ENV GOARCH arm64 +ENV LIBRARY_PATH /usr/local/cuda/lib64/stubs +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CUDA_VARIANT="_jetpack5" \ + CUDA_DIST_DIR="/go/src/github.com/ollama/ollama/dist/linux-arm64/ollama_libs/cuda_jetpack5" \ + CMAKE_CUDA_ARCHITECTURES="72;87" \ bash gen_linux.sh FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete AS rocm-build-amd64 @@ -123,8 +155,14 @@ ARG GOLANG_VERSION WORKDIR /go/src/github.com/ollama/ollama COPY . . COPY --from=static-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ -COPY --from=cuda-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ +COPY --from=cuda-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +## arm binary += 381M +COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ +## arm binary += 330M +COPY --from=cuda-build-jetpack5-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-build-jetpack5-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ ARG GOFLAGS ARG CGO_CFLAGS RUN --mount=type=cache,target=/root/.ccache \ diff --git a/gpu/gpu.go b/gpu/gpu.go index d0ae0f34..22461922 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -15,7 +15,9 @@ import ( "log/slog" "os" "path/filepath" + "regexp" "runtime" + "strconv" "strings" "sync" "unsafe" @@ -215,7 +217,7 @@ func GetGPUInfo() GpuInfoList { GpuInfo: GpuInfo{ memInfo: mem, Library: "cpu", - Variant: cpuCapability, + Variant: cpuCapability.String(), ID: "0", }, }, @@ -231,6 +233,35 @@ func GetGPUInfo() GpuInfoList { depPath := GetDepDir() + var cudaVariant string + if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" { + if CudaTegra != "" { + ver := strings.Split(CudaTegra, ".") + if len(ver) > 0 { + cudaVariant = "jetpack" + ver[0] + } + } else if data, err := os.ReadFile("/etc/nv_tegra_release"); err == nil { + r := regexp.MustCompile(` R(\d+) `) + m := r.FindSubmatch(data) + if len(m) != 2 { + slog.Info("Unexpected format for /etc/nv_tegra_release. Set JETSON_JETPACK to select version") + } else { + if l4t, err := strconv.Atoi(string(m[1])); err == nil { + // Note: mapping from L4t -> JP is inconsistent (can't just subtract 30) + // https://developer.nvidia.com/embedded/jetpack-archive + switch l4t { + case 35: + cudaVariant = "jetpack5" + case 36: + cudaVariant = "jetpack6" + default: + slog.Info("unsupported L4T version", "nv_tegra_release", string(data)) + } + } + } + } + } + // Load ALL libraries cHandles = initCudaHandles() @@ -240,6 +271,7 @@ func GetGPUInfo() GpuInfoList { gpuInfo := CudaGPUInfo{ GpuInfo: GpuInfo{ Library: "cuda", + Variant: cudaVariant, }, index: i, } @@ -266,7 +298,15 @@ func GetGPUInfo() GpuInfoList { gpuInfo.ID = C.GoString(&memInfo.gpu_id[0]) gpuInfo.Compute = fmt.Sprintf("%d.%d", memInfo.major, memInfo.minor) gpuInfo.MinimumMemory = cudaMinimumMemory - gpuInfo.DependencyPath = depPath + if depPath != "" { + gpuInfo.DependencyPath = depPath + // Check for variant specific directory + if cudaVariant != "" { + if _, err := os.Stat(filepath.Join(depPath, "cuda_"+cudaVariant)); err == nil { + gpuInfo.DependencyPath = filepath.Join(depPath, "cuda_"+cudaVariant) + } + } + } gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) gpuInfo.DriverMajor = driverMajor gpuInfo.DriverMinor = driverMinor diff --git a/gpu/gpu_darwin.go b/gpu/gpu_darwin.go index 9d9fd84e..417b48df 100644 --- a/gpu/gpu_darwin.go +++ b/gpu/gpu_darwin.go @@ -25,7 +25,7 @@ func GetGPUInfo() GpuInfoList { return []GpuInfo{ { Library: "cpu", - Variant: GetCPUCapability(), + Variant: GetCPUCapability().String(), memInfo: mem, }, } @@ -48,7 +48,7 @@ func GetCPUInfo() GpuInfoList { return []GpuInfo{ { Library: "cpu", - Variant: GetCPUCapability(), + Variant: GetCPUCapability().String(), memInfo: mem, }, } diff --git a/gpu/types.go b/gpu/types.go index 8d22b06b..fc628d47 100644 --- a/gpu/types.go +++ b/gpu/types.go @@ -19,7 +19,7 @@ type GpuInfo struct { Library string `json:"library,omitempty"` // Optional variant to select (e.g. versions, cpu feature flags) - Variant CPUCapability `json:"variant"` + Variant string `json:"variant"` // MinimumMemory represents the minimum memory required to use the GPU MinimumMemory uint64 `json:"-"` @@ -81,8 +81,8 @@ func (l GpuInfoList) ByLibrary() []GpuInfoList { for _, info := range l { found := false requested := info.Library - if info.Variant != CPUCapabilityNone { - requested += "_" + info.Variant.String() + if info.Variant != CPUCapabilityNone.String() { + requested += "_" + info.Variant } for i, lib := range libs { if lib == requested { diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index 1365d07d..dc9dda5a 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -165,7 +165,7 @@ if [ -z "${OLLAMA_SKIP_CUDA_GENERATE}" -a -d "${CUDA_LIB_DIR}" ]; then echo "CUDA libraries detected - building dynamic CUDA library" init_vars CUDA_MAJOR=$(ls "${CUDA_LIB_DIR}"/libcudart.so.* | head -1 | cut -f3 -d. || true) - if [ -n "${CUDA_MAJOR}" ]; then + if [ -n "${CUDA_MAJOR}" -a -z "${CUDA_VARIANT}" ]; then CUDA_VARIANT=_v${CUDA_MAJOR} fi if [ "${ARCH}" == "arm64" ]; then @@ -189,9 +189,10 @@ if [ -z "${OLLAMA_SKIP_CUDA_GENERATE}" -a -d "${CUDA_LIB_DIR}" ]; then CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} ${ARM64_DEFS} ${CMAKE_CUDA_DEFS} -DGGML_STATIC=off" BUILD_DIR="../build/linux/${ARCH}/cuda${CUDA_VARIANT}" export LLAMA_SERVER_LDFLAGS="-L${CUDA_LIB_DIR} -lcudart -lcublas -lcublasLt -lcuda" - CUDA_DIST_DIR="${DIST_BASE}/ollama_libs" + CUDA_DIST_DIR="${CUDA_DIST_DIR:-${DIST_BASE}/ollama_libs}" build install + echo "Installing CUDA dependencies in ${CUDA_DIST_DIR}" mkdir -p "${CUDA_DIST_DIR}" for lib in ${CUDA_LIB_DIR}/libcudart.so* ${CUDA_LIB_DIR}/libcublas.so* ${CUDA_LIB_DIR}/libcublasLt.so* ; do cp -a "${lib}" "${CUDA_DIST_DIR}" diff --git a/llm/payload.go b/llm/payload.go index b402e1f2..963b3295 100644 --- a/llm/payload.go +++ b/llm/payload.go @@ -82,8 +82,8 @@ func serversForGpu(info gpu.GpuInfo) []string { // glob workDir for files that start with ollama_ availableServers := getAvailableServers() requested := info.Library - if info.Variant != gpu.CPUCapabilityNone { - requested += "_" + info.Variant.String() + if info.Variant != gpu.CPUCapabilityNone.String() { + requested += "_" + info.Variant } servers := []string{} diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh index ebb60c5a..adda2ad7 100755 --- a/scripts/build_linux.sh +++ b/scripts/build_linux.sh @@ -22,6 +22,7 @@ for TARGETARCH in ${BUILD_ARCH}; do -t builder:$TARGETARCH \ . docker create --platform linux/$TARGETARCH --name builder-$TARGETARCH builder:$TARGETARCH + rm -rf ./dist/linux-$TARGETARCH docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/dist/linux-$TARGETARCH ./dist docker rm builder-$TARGETARCH echo "Compressing final linux bundle..." From fc3b4cda89f468f923e2e6095c6a62a5c3c336ff Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Wed, 19 Jun 2024 09:36:30 -0700 Subject: [PATCH 049/174] Report GPU variant in log --- gpu/types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gpu/types.go b/gpu/types.go index fc628d47..88539078 100644 --- a/gpu/types.go +++ b/gpu/types.go @@ -105,6 +105,7 @@ func (l GpuInfoList) LogDetails() { slog.Info("inference compute", "id", g.ID, "library", g.Library, + "variant", g.Variant, "compute", g.Compute, "driver", fmt.Sprintf("%d.%d", g.DriverMajor, g.DriverMinor), "name", g.Name, From 4fe3a556faf790ba993223cfdda16e281b6cb76d Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 13 Jun 2024 20:46:14 -0700 Subject: [PATCH 050/174] Add cuda v12 variant and selection logic Based on compute capability and driver version, pick v12 or v11 cuda variants. --- Dockerfile | 43 +++++++++++++++++++++++++++++++++---------- gpu/cuda_common.go | 43 +++++++++++++++++++++++++++++++++++++++++++ gpu/gpu.go | 40 ++++------------------------------------ gpu/types.go | 6 ++++-- 4 files changed, 84 insertions(+), 48 deletions(-) diff --git a/Dockerfile b/Dockerfile index 79b2a696..e200f5d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ARG GOLANG_VERSION=1.22.5 ARG CMAKE_VERSION=3.22.1 -# this CUDA_VERSION corresponds with the one specified in docs/gpu.md -ARG CUDA_VERSION=11.3.1 +ARG CUDA_VERSION_11=11.3.1 +ARG CUDA_VERSION_12=12.4.0 ARG ROCM_VERSION=6.1.2 ARG JETPACK_6=r36.2.0 ARG JETPACK_5=r35.4.1 @@ -13,7 +13,7 @@ COPY .git .git COPY .gitmodules .gitmodules COPY llm llm -FROM --platform=linux/amd64 nvidia/cuda:$CUDA_VERSION-devel-centos7 AS cuda-build-amd64 +FROM --platform=linux/amd64 nvidia/cuda:$CUDA_VERSION_11-devel-centos7 AS cuda-11-build-amd64 ARG CMAKE_VERSION COPY ./scripts/rh_linux_deps.sh / RUN CMAKE_VERSION=${CMAKE_VERSION} sh /rh_linux_deps.sh @@ -23,9 +23,29 @@ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ - OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh + OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CMAKE_CUDA_ARCHITECTURES="50;52;53;60;61;62;70;72;75;80;86" \ + CUDA_VARIANT="_v11" \ + bash gen_linux.sh -FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION-devel-rockylinux8 AS cuda-build-server-arm64 +FROM --platform=linux/amd64 nvidia/cuda:$CUDA_VERSION_12-devel-centos7 AS cuda-12-build-amd64 +ARG CMAKE_VERSION +COPY ./scripts/rh_linux_deps.sh / +RUN CMAKE_VERSION=${CMAKE_VERSION} sh /rh_linux_deps.sh +ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH +COPY --from=llm-code / /go/src/github.com/ollama/ollama/ +WORKDIR /go/src/github.com/ollama/ollama/llm/generate +ARG CGO_CFLAGS +ENV GOARCH amd64 +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CMAKE_CUDA_ARCHITECTURES="60;61;62;70;72;75;80;86;87;89;90;90a" \ + CUDA_VARIANT="_v12" \ + bash gen_linux.sh + +FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION_11-devel-rockylinux8 AS cuda-11-build-server-arm64 ARG CMAKE_VERSION COPY ./scripts/rh_linux_deps.sh / RUN CMAKE_VERSION=${CMAKE_VERSION} sh /rh_linux_deps.sh @@ -34,7 +54,8 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ENV GOARCH arm64 -RUN OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh +RUN --mount=type=cache,target=/root/.ccache \ + OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_6} AS cuda-build-jetpack6-arm64 ARG CMAKE_VERSION @@ -139,8 +160,10 @@ COPY . . COPY --from=static-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cpu_avx-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cpu_avx2-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=cuda-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ -COPY --from=cuda-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-11-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ +COPY --from=cuda-11-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-12-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ +COPY --from=cuda-12-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ARG GOFLAGS @@ -155,8 +178,8 @@ ARG GOLANG_VERSION WORKDIR /go/src/github.com/ollama/ollama COPY . . COPY --from=static-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=cuda-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ -COPY --from=cuda-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ +COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ## arm binary += 381M COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ diff --git a/gpu/cuda_common.go b/gpu/cuda_common.go index c90a644c..defaa60a 100644 --- a/gpu/cuda_common.go +++ b/gpu/cuda_common.go @@ -4,9 +4,17 @@ package gpu import ( "log/slog" + "os" + "regexp" + "runtime" + "strconv" "strings" ) +// Jetson devices have JETSON_JETPACK="x.y.z" factory set to the Jetpack version installed. +// Included to drive logic for reducing Ollama-allocated overhead on L4T/Jetson devices. +var CudaTegra string = os.Getenv("JETSON_JETPACK") + func cudaGetVisibleDevicesEnv(gpuInfo []GpuInfo) (string, string) { ids := []string{} for _, info := range gpuInfo { @@ -19,3 +27,38 @@ func cudaGetVisibleDevicesEnv(gpuInfo []GpuInfo) (string, string) { } return "CUDA_VISIBLE_DEVICES", strings.Join(ids, ",") } + +func cudaGetVariant(gpuInfo CudaGPUInfo) string { + if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" { + if CudaTegra != "" { + ver := strings.Split(CudaTegra, ".") + if len(ver) > 0 { + return "jetpack" + ver[0] + } + } else if data, err := os.ReadFile("/etc/nv_tegra_release"); err == nil { + r := regexp.MustCompile(` R(\d+) `) + m := r.FindSubmatch(data) + if len(m) != 2 { + slog.Info("Unexpected format for /etc/nv_tegra_release. Set JETSON_JETPACK to select version") + } else { + if l4t, err := strconv.Atoi(string(m[1])); err == nil { + // Note: mapping from L4t -> JP is inconsistent (can't just subtract 30) + // https://developer.nvidia.com/embedded/jetpack-archive + switch l4t { + case 35: + return "jetpack5" + case 36: + return "jetpack6" + default: + slog.Info("unsupported L4T version", "nv_tegra_release", string(data)) + } + } + } + } + } + + if gpuInfo.computeMajor < 6 || gpuInfo.DriverMajor < 12 { + return "v11" + } + return "v12" +} diff --git a/gpu/gpu.go b/gpu/gpu.go index 22461922..eb87807a 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -15,9 +15,7 @@ import ( "log/slog" "os" "path/filepath" - "regexp" "runtime" - "strconv" "strings" "sync" "unsafe" @@ -66,10 +64,6 @@ var RocmComputeMin = 9 // TODO find a better way to detect iGPU instead of minimum memory const IGPUMemLimit = 1 * format.GibiByte // 512G is what they typically report, so anything less than 1G must be iGPU -// Jetson devices have JETSON_JETPACK="x.y.z" factory set to the Jetpack version installed. -// Included to drive logic for reducing Ollama-allocated overhead on L4T/Jetson devices. -var CudaTegra string = os.Getenv("JETSON_JETPACK") - // Note: gpuMutex must already be held func initCudaHandles() *cudaHandles { // TODO - if the ollama build is CPU only, don't do these checks as they're irrelevant and confusing @@ -233,35 +227,6 @@ func GetGPUInfo() GpuInfoList { depPath := GetDepDir() - var cudaVariant string - if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" { - if CudaTegra != "" { - ver := strings.Split(CudaTegra, ".") - if len(ver) > 0 { - cudaVariant = "jetpack" + ver[0] - } - } else if data, err := os.ReadFile("/etc/nv_tegra_release"); err == nil { - r := regexp.MustCompile(` R(\d+) `) - m := r.FindSubmatch(data) - if len(m) != 2 { - slog.Info("Unexpected format for /etc/nv_tegra_release. Set JETSON_JETPACK to select version") - } else { - if l4t, err := strconv.Atoi(string(m[1])); err == nil { - // Note: mapping from L4t -> JP is inconsistent (can't just subtract 30) - // https://developer.nvidia.com/embedded/jetpack-archive - switch l4t { - case 35: - cudaVariant = "jetpack5" - case 36: - cudaVariant = "jetpack6" - default: - slog.Info("unsupported L4T version", "nv_tegra_release", string(data)) - } - } - } - } - } - // Load ALL libraries cHandles = initCudaHandles() @@ -271,7 +236,6 @@ func GetGPUInfo() GpuInfoList { gpuInfo := CudaGPUInfo{ GpuInfo: GpuInfo{ Library: "cuda", - Variant: cudaVariant, }, index: i, } @@ -297,7 +261,10 @@ func GetGPUInfo() GpuInfoList { gpuInfo.FreeMemory = uint64(memInfo.free) gpuInfo.ID = C.GoString(&memInfo.gpu_id[0]) gpuInfo.Compute = fmt.Sprintf("%d.%d", memInfo.major, memInfo.minor) + gpuInfo.computeMajor = int(memInfo.major) + gpuInfo.computeMinor = int(memInfo.minor) gpuInfo.MinimumMemory = cudaMinimumMemory + cudaVariant := cudaGetVariant(gpuInfo) if depPath != "" { gpuInfo.DependencyPath = depPath // Check for variant specific directory @@ -310,6 +277,7 @@ func GetGPUInfo() GpuInfoList { gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) gpuInfo.DriverMajor = driverMajor gpuInfo.DriverMinor = driverMinor + gpuInfo.Variant = cudaGetVariant(gpuInfo) // query the management library as well so we can record any skew between the two // which represents overhead on the GPU we must set aside on subsequent updates diff --git a/gpu/types.go b/gpu/types.go index 88539078..4cbbeb84 100644 --- a/gpu/types.go +++ b/gpu/types.go @@ -53,8 +53,10 @@ type CPUInfo struct { type CudaGPUInfo struct { GpuInfo - OSOverhead uint64 // Memory overhead between the driver library and management library - index int //nolint:unused,nolintlint + OSOverhead uint64 // Memory overhead between the driver library and management library + index int //nolint:unused,nolintlint + computeMajor int //nolint:unused,nolintlint + computeMinor int //nolint:unused,nolintlint } type CudaGPUInfoList []CudaGPUInfo From f6c811b32075cb3b7633d7d4213251d474a77682 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 12 Jul 2024 11:35:41 -0700 Subject: [PATCH 051/174] Enable cuda v12 flags --- Dockerfile | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e200f5d4..e83a266a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ ARG GOLANG_VERSION=1.22.5 ARG CMAKE_VERSION=3.22.1 ARG CUDA_VERSION_11=11.3.1 +ARG CUDA_V11_ARCHITECTURES="50;52;53;60;61;62;70;72;75;80;86" ARG CUDA_VERSION_12=12.4.0 +ARG CUDA_V12_ARCHITECTURES="60;61;62;70;72;75;80;86;87;89;90;90a" ARG ROCM_VERSION=6.1.2 ARG JETPACK_6=r36.2.0 ARG JETPACK_5=r35.4.1 @@ -21,11 +23,12 @@ ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS +ARG CUDA_V11_ARCHITECTURES ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ - CMAKE_CUDA_ARCHITECTURES="50;52;53;60;61;62;70;72;75;80;86" \ + CMAKE_CUDA_ARCHITECTURES="${CUDA_V11_ARCHITECTURES}" \ CUDA_VARIANT="_v11" \ bash gen_linux.sh @@ -37,12 +40,14 @@ ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS +ARG CUDA_V12_ARCHITECTURES ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ - CMAKE_CUDA_ARCHITECTURES="60;61;62;70;72;75;80;86;87;89;90;90a" \ + CMAKE_CUDA_ARCHITECTURES="${CUDA_V12_ARCHITECTURES}" \ CUDA_VARIANT="_v12" \ + OLLAMA_CUSTOM_CUDA_DEFS="-DGGML_CUDA_USE_GRAPHS=on" \ bash gen_linux.sh FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION_11-devel-rockylinux8 AS cuda-11-build-server-arm64 @@ -53,9 +58,31 @@ ENV PATH /opt/rh/gcc-toolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS +ARG CUDA_V11_ARCHITECTURES +ENV GOARCH arm64 +RUN OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CMAKE_CUDA_ARCHITECTURES="${CUDA_V11_ARCHITECTURES}" \ + CUDA_VARIANT="_v11" \ + bash gen_linux.sh + +FROM --platform=linux/arm64 nvidia/cuda:$CUDA_VERSION_12-devel-rockylinux8 AS cuda-12-build-server-arm64 +ARG CMAKE_VERSION +COPY ./scripts/rh_linux_deps.sh / +RUN CMAKE_VERSION=${CMAKE_VERSION} sh /rh_linux_deps.sh +ENV PATH /opt/rh/gcc-toolset-10/root/usr/bin:$PATH +COPY --from=llm-code / /go/src/github.com/ollama/ollama/ +WORKDIR /go/src/github.com/ollama/ollama/llm/generate +ARG CGO_CFLAGS +ARG CUDA_V12_ARCHITECTURES ENV GOARCH arm64 RUN --mount=type=cache,target=/root/.ccache \ - OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh + OLLAMA_SKIP_STATIC_GENERATE=1 \ + OLLAMA_SKIP_CPU_GENERATE=1 \ + CMAKE_CUDA_ARCHITECTURES="${CUDA_V12_ARCHITECTURES}" \ + CUDA_VARIANT="_v12" \ + OLLAMA_CUSTOM_CUDA_DEFS="-DGGML_CUDA_USE_GRAPHS=on" \ + bash gen_linux.sh FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_6} AS cuda-build-jetpack6-arm64 ARG CMAKE_VERSION @@ -180,6 +207,8 @@ COPY . . COPY --from=static-build-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ +COPY --from=cuda-12-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ +COPY --from=cuda-12-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ ## arm binary += 381M COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ From 927d98a6cde43ffee3ef269cf013df5e96cbe767 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 12 Jul 2024 14:33:13 -0700 Subject: [PATCH 052/174] Add windows cuda v12 + v11 support --- .github/workflows/release.yaml | 93 ++++++++++++++++++++++++++++++++-- llm/generate/gen_windows.ps1 | 6 +-- scripts/build_windows.ps1 | 63 ++++++++++++++++++----- 3 files changed, 142 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9287f6f7..4bd68455 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -183,8 +183,8 @@ jobs: name: windows-rocm-deps path: dist/deps/* - # CUDA generation step - generate-windows-cuda: + # CUDA v11 generation step + generate-windows-cuda-v11: environment: release runs-on: windows env: @@ -256,7 +256,89 @@ jobs: cp "${NVIDIA_DIR}\cublasLt64_*.dll" "dist\deps\" - uses: actions/upload-artifact@v4 with: - name: generate-windows-cuda + name: generate-windows-cuda-v11 + path: | + llm/build/**/bin/* + dist/windows-amd64/** + - uses: actions/upload-artifact@v4 + with: + name: windows-cuda-deps + path: dist/deps/* + + # CUDA v12 generation step + generate-windows-cuda-v12: + environment: release + runs-on: windows + env: + KEY_CONTAINER: ${{ vars.KEY_CONTAINER }} + steps: + - uses: actions/checkout@v4 + - name: Set Version + shell: bash + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + - uses: 'google-github-actions/auth@v2' + with: + project_id: 'ollama' + credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}' + - run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt + - name: install Windows SDK 8.1 to get signtool + run: | + $ErrorActionPreference = "Stop" + write-host "downloading SDK" + Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe" + Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait + write-host "Win SDK 8.1 installed" + gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe' + - name: install signing plugin + run: | + $ErrorActionPreference = "Stop" + write-host "downloading plugin" + Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip" + Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\ + write-host "Installing plugin" + & "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet + write-host "plugin installed" + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + - name: 'Install CUDA' + run: | + $ErrorActionPreference = "Stop" + write-host "downloading CUDA Installer" + Invoke-WebRequest -Uri "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_551.61_windows.exe" -OutFile "${env:RUNNER_TEMP}\cuda-install.exe" + write-host "Installing CUDA" + Start-Process "${env:RUNNER_TEMP}\cuda-install.exe" -ArgumentList '-s' -NoNewWindow -Wait + write-host "Completed CUDA" + $cudaPath=((resolve-path "c:\Program Files\NVIDIA*\CUDA\v*\bin\nvcc.exe")[0].path | split-path | split-path) + $cudaVer=($cudaPath | split-path -leaf ) -replace 'v(\d+).(\d+)', '$1_$2' + echo "$cudaPath\bin" >> $env:GITHUB_PATH + echo "CUDA_PATH=$cudaPath" >> $env:GITHUB_ENV + echo "CUDA_PATH_V${cudaVer}=$cudaPath" >> $env:GITHUB_ENV + echo "CUDA_PATH_VX_Y=CUDA_PATH_V${cudaVer}" >> $env:GITHUB_ENV + - name: 'Verify CUDA' + run: nvcc -V + - run: go get ./... + - name: go generate + run: | + $gopath=(get-command go).source | split-path -parent + $cudabin=(get-command nvcc).source | split-path + & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1" + cd $env:GITHUB_WORKSPACE + $env:CMAKE_SYSTEM_VERSION="10.0.22621.0" + $env:PATH="$gopath;$cudabin;$env:PATH" + $env:OLLAMA_SKIP_CPU_GENERATE="1" + go generate -x ./... + - name: 'gather cuda dependencies' + run: | + $NVIDIA_DIR=(resolve-path 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\*\bin\')[0] + md "dist\deps" + cp "${NVIDIA_DIR}\cudart64_*.dll" "dist\deps\" + cp "${NVIDIA_DIR}\cublas64_*.dll" "dist\deps\" + cp "${NVIDIA_DIR}\cublasLt64_*.dll" "dist\deps\" + - uses: actions/upload-artifact@v4 + with: + name: generate-windows-cuda-v12 path: | llm/build/**/bin/* dist/windows-amd64/** @@ -270,7 +352,8 @@ jobs: environment: release runs-on: windows needs: - - generate-windows-cuda + - generate-windows-cuda-v11 + - generate-windows-cuda-v12 - generate-windows-rocm - generate-windows-cpu env: @@ -314,7 +397,7 @@ jobs: name: generate-windows-cpu - uses: actions/download-artifact@v4 with: - name: generate-windows-cuda + name: generate-windows-cuda-v11 - uses: actions/download-artifact@v4 with: name: windows-cuda-deps diff --git a/llm/generate/gen_windows.ps1 b/llm/generate/gen_windows.ps1 index 1f8c96d8..42708d3e 100644 --- a/llm/generate/gen_windows.ps1 +++ b/llm/generate/gen_windows.ps1 @@ -261,7 +261,7 @@ function build_cuda() { if ((-not "${env:OLLAMA_SKIP_CUDA_GENERATE}") -and ("${script:CUDA_LIB_DIR}")) { # Then build cuda as a dynamically loaded library $nvcc = "$script:CUDA_LIB_DIR\nvcc.exe" - $script:CUDA_VERSION=(get-item ($nvcc | split-path | split-path)).Basename + $script:CUDA_VERSION=((get-item ($nvcc | split-path | split-path)).Basename -Split "\.")[0] if ($null -ne $script:CUDA_VERSION) { $script:CUDA_VARIANT="_"+$script:CUDA_VERSION } @@ -273,9 +273,9 @@ function build_cuda() { "-DGGML_CUDA=ON", "-DGGML_AVX=on", "-DGGML_AVX2=off", - "-DCUDAToolkit_INCLUDE_DIR=$script:CUDA_INCLUDE_DIR", "-DCMAKE_CUDA_FLAGS=-t8", - "-DCMAKE_CUDA_ARCHITECTURES=${script:CMAKE_CUDA_ARCHITECTURES}" + "-DCMAKE_CUDA_ARCHITECTURES=${script:CMAKE_CUDA_ARCHITECTURES}", + "-DCMAKE_CUDA_COMPILER_TOOLKIT_ROOT=$env:CUDA_PATH" ) if ($null -ne $env:OLLAMA_CUSTOM_CUDA_DEFS) { write-host "OLLAMA_CUSTOM_CUDA_DEFS=`"${env:OLLAMA_CUSTOM_CUDA_DEFS}`"" diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1 index e8d851f4..50b60230 100644 --- a/scripts/build_windows.ps1 +++ b/scripts/build_windows.ps1 @@ -7,6 +7,7 @@ $ErrorActionPreference = "Stop" function checkEnv() { + $script:ARCH = $Env:PROCESSOR_ARCHITECTURE.ToLower() $script:TARGET_ARCH=$Env:PROCESSOR_ARCHITECTURE.ToLower() Write-host "Building for ${script:TARGET_ARCH}" write-host "Locating required tools and paths" @@ -15,26 +16,23 @@ function checkEnv() { $MSVC_INSTALL=(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation $env:VCToolsRedistDir=(get-item "${MSVC_INSTALL}\VC\Redist\MSVC\*")[0] } - # Try to find the CUDA dir - if ($null -eq $env:NVIDIA_DIR) { + # Locate CUDA versions + # Note: this assumes every version found will be built + $cudaList=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\" -ea 'silentlycontinue') + if ($cudaList.length -eq 0) { $d=(get-command -ea 'silentlycontinue' nvcc).path - if ($d -ne $null) { - $script:NVIDIA_DIR=($d| split-path -parent) - } else { - $cudaList=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\" -ea 'silentlycontinue') - if ($cudaList.length > 0) { - $script:NVIDIA_DIR=$cudaList[0] - } + if ($null -ne $d) { + $script:CUDA_DIRS=@($d| split-path -parent) } } else { - $script:NVIDIA_DIR=$env:NVIDIA_DIR + $script:CUDA_DIRS=$cudaList } $script:INNO_SETUP_DIR=(get-item "C:\Program Files*\Inno Setup*\")[0] $script:DEPS_DIR="${script:SRC_DIR}\dist\windows-${script:TARGET_ARCH}" $env:CGO_ENABLED="1" - echo "Checking version" + Write-Output "Checking version" if (!$env:VERSION) { $data=(git describe --tags --first-parent --abbrev=7 --long --dirty --always) $pattern="v(.+)" @@ -71,7 +69,48 @@ function checkEnv() { function buildOllama() { write-host "Building ollama CLI" if ($null -eq ${env:OLLAMA_SKIP_GENERATE}) { - & go generate ./... + Remove-Item -ea 0 -recurse -force -path "${script:SRC_DIR}\dist\windows-${script:ARCH}" + + # TODO - consider trying to parallelize this with Start-ThreadJob, but env vars can't be used to toggle + # which targets to build + + # Start by skipping CUDA to build everything else + pwsh -Command { $env:OLLAMA_SKIP_CUDA_GENERATE="1"; & go generate ./... } + if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)} + + # Then skip everyhting else and build all the CUDA variants + foreach ($env:CUDA_LIB_DIR in $script:CUDA_DIRS) { + write-host "Building CUDA ${env:CUDA_LIB_DIR}" + + if ($env:CUDA_LIB_DIR.Contains("v12")) { + pwsh -Command { + $env:OLLAMA_SKIP_CUDA_GENERATE="" + $env:OLLAMA_SKIP_STATIC_GENERATE="1" + $env:OLLAMA_SKIP_CPU_GENERATE="1" + $env:OLLAMA_SKIP_ONEAPI_GENERATE="1" + $env:OLLAMA_SKIP_ROCM_GENERATE="1" + $env:CMAKE_CUDA_ARCHITECTURES="60;61;62;70;72;75;80;86;87;89;90;90a" + $env:OLLAMA_CUSTOM_CUDA_DEFS="-DGGML_CUDA_USE_GRAPHS=on" + $env:CUDA_PATH=split-path -path $env:CUDA_LIB_DIR -parent + $env:PATH="$envs:CUDA_LIB_DIR;$env:PATH" + & go generate ./... + } + } else { + pwsh -Command { + $env:OLLAMA_SKIP_CUDA_GENERATE="" + $env:OLLAMA_SKIP_STATIC_GENERATE="1" + $env:OLLAMA_SKIP_CPU_GENERATE="1" + $env:OLLAMA_SKIP_ONEAPI_GENERATE="1" + $env:OLLAMA_SKIP_ROCM_GENERATE="1" + $env:CMAKE_CUDA_ARCHITECTURES="50;52;53;60;61;62;70;72;75;80;86" + $env:OLLAMA_CUSTOM_CUDA_DEFS="" + $env:CUDA_PATH=split-path -path $env:CUDA_LIB_DIR -parent + $env:PATH="$envs:CUDA_LIB_DIR;$env:PATH" + & go generate ./... + } + } + if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)} + } if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)} } else { write-host "Skipping generate step with OLLAMA_SKIP_GENERATE set" From 3b19cdba2a090772b2e886dbfbf712992fafe0cd Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 13 Aug 2024 13:30:28 -0700 Subject: [PATCH 053/174] Remove Jetpack --- Dockerfile | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/Dockerfile b/Dockerfile index e83a266a..99ba5b65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,9 +5,6 @@ ARG CUDA_V11_ARCHITECTURES="50;52;53;60;61;62;70;72;75;80;86" ARG CUDA_VERSION_12=12.4.0 ARG CUDA_V12_ARCHITECTURES="60;61;62;70;72;75;80;86;87;89;90;90a" ARG ROCM_VERSION=6.1.2 -ARG JETPACK_6=r36.2.0 -ARG JETPACK_5=r35.4.1 -ARG JETPACK_4=r32.7.1 # Copy the minimal context we need to run the generate scripts FROM scratch AS llm-code @@ -84,39 +81,6 @@ RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_CUSTOM_CUDA_DEFS="-DGGML_CUDA_USE_GRAPHS=on" \ bash gen_linux.sh -FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_6} AS cuda-build-jetpack6-arm64 -ARG CMAKE_VERSION -RUN apt-get update && apt-get install -y git curl && \ - curl -s -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz | tar -zx -C /usr --strip-components 1 -COPY --from=llm-code / /go/src/github.com/ollama/ollama/ -WORKDIR /go/src/github.com/ollama/ollama/llm/generate -ARG CGO_CFLAGS -ENV GOARCH arm64 -ENV LIBRARY_PATH /usr/local/cuda/lib64/stubs -RUN --mount=type=cache,target=/root/.ccache \ - OLLAMA_SKIP_STATIC_GENERATE=1 \ - OLLAMA_SKIP_CPU_GENERATE=1 \ - CUDA_VARIANT="_jetpack6" \ - CUDA_DIST_DIR="/go/src/github.com/ollama/ollama/dist/linux-arm64/ollama_libs/cuda_jetpack6" \ - CMAKE_CUDA_ARCHITECTURES="87" \ - bash gen_linux.sh - -FROM --platform=linux/arm64 nvcr.io/nvidia/l4t-jetpack:${JETPACK_5} AS cuda-build-jetpack5-arm64 -ARG CMAKE_VERSION -RUN apt-get update && apt-get install -y git curl && \ - curl -s -L https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz | tar -zx -C /usr --strip-components 1 -COPY --from=llm-code / /go/src/github.com/ollama/ollama/ -WORKDIR /go/src/github.com/ollama/ollama/llm/generate -ARG CGO_CFLAGS -ENV GOARCH arm64 -ENV LIBRARY_PATH /usr/local/cuda/lib64/stubs -RUN --mount=type=cache,target=/root/.ccache \ - OLLAMA_SKIP_STATIC_GENERATE=1 \ - OLLAMA_SKIP_CPU_GENERATE=1 \ - CUDA_VARIANT="_jetpack5" \ - CUDA_DIST_DIR="/go/src/github.com/ollama/ollama/dist/linux-arm64/ollama_libs/cuda_jetpack5" \ - CMAKE_CUDA_ARCHITECTURES="72;87" \ - bash gen_linux.sh FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete AS rocm-build-amd64 ARG CMAKE_VERSION @@ -209,12 +173,6 @@ COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ di COPY --from=cuda-11-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ COPY --from=cuda-12-build-server-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ COPY --from=cuda-12-build-server-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -## arm binary += 381M -COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=cuda-build-jetpack6-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ -## arm binary += 330M -COPY --from=cuda-build-jetpack5-arm64 /go/src/github.com/ollama/ollama/llm/build/linux/ llm/build/linux/ -COPY --from=cuda-build-jetpack5-arm64 /go/src/github.com/ollama/ollama/dist/ dist/ ARG GOFLAGS ARG CGO_CFLAGS RUN --mount=type=cache,target=/root/.ccache \ From 88bb9e332877dfbba40030c19570fdbe00f41a21 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Wed, 14 Aug 2024 16:32:57 -0700 Subject: [PATCH 054/174] Adjust layout to bin+lib/ollama --- Dockerfile | 23 ++++++++++++++------ app/ollama.iss | 12 +++++------ docs/linux.md | 10 ++++----- envconfig/config.go | 6 +++--- gpu/amd_common.go | 2 +- gpu/amd_windows.go | 2 +- gpu/gpu.go | 4 ++-- llm/generate/gen_linux.sh | 6 +++--- llm/generate/gen_windows.ps1 | 42 ++++++++++++++++++------------------ scripts/build_windows.ps1 | 16 +++++++------- scripts/install.sh | 14 +++++++----- 11 files changed, 74 insertions(+), 63 deletions(-) diff --git a/Dockerfile b/Dockerfile index 99ba5b65..d4b86918 100644 --- a/Dockerfile +++ b/Dockerfile @@ -95,8 +95,8 @@ ARG AMDGPU_TARGETS ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh -RUN mkdir -p ../../dist/linux-amd64/ollama_libs && \ - (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64/ollama_libs && tar xf - ) +RUN mkdir -p ../../dist/linux-amd64/lib/ollama && \ + (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64/lib/ollama && tar xf - ) FROM --platform=linux/amd64 centos:7 AS cpu-builder-amd64 ARG CMAKE_VERSION @@ -160,7 +160,7 @@ COPY --from=rocm-build-amd64 /go/src/github.com/ollama/ollama/llm/build/linux/ l ARG GOFLAGS ARG CGO_CFLAGS RUN --mount=type=cache,target=/root/.ccache \ - go build -trimpath -o dist/linux-amd64/ollama . + go build -trimpath -o dist/linux-amd64/bin/ollama . # Intermediate stage used for ./scripts/build_linux.sh FROM --platform=linux/arm64 cpu-build-arm64 AS build-arm64 @@ -176,20 +176,29 @@ COPY --from=cuda-12-build-server-arm64 /go/src/github.com/ollama/ollama/llm/buil ARG GOFLAGS ARG CGO_CFLAGS RUN --mount=type=cache,target=/root/.ccache \ - go build -trimpath -o dist/linux-arm64/ollama . + go build -trimpath -o dist/linux-arm64/bin/ollama . + +# Strip out ROCm dependencies to keep the primary image lean +FROM --platform=linux/amd64 ubuntu:22.04 as amd64-libs-without-rocm +COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/lib/ /scratch/ +RUN cd /scratch/ollama/ && rm -rf rocblas libamd* libdrm* libroc* libhip* libhsa* # Runtime stages FROM --platform=linux/amd64 ubuntu:22.04 as runtime-amd64 +COPY --from=amd64-libs-without-rocm /scratch/ /lib/ RUN apt-get update && apt-get install -y ca-certificates -COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/ollama /bin/ollama +COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/bin/ /bin/ + FROM --platform=linux/arm64 ubuntu:22.04 as runtime-arm64 +COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/lib/ /lib/ RUN apt-get update && apt-get install -y ca-certificates -COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/ollama /bin/ollama +COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/bin/ /bin/ # Radeon images are much larger so we keep it distinct from the CPU/CUDA image FROM --platform=linux/amd64 rocm/dev-centos-7:${ROCM_VERSION}-complete as runtime-rocm RUN update-pciids -COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/ollama /bin/ollama +COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/bin/ /bin/ +RUN ln -s /opt/rocm/lib /lib/ollama EXPOSE 11434 ENV OLLAMA_HOST 0.0.0.0 diff --git a/app/ollama.iss b/app/ollama.iss index e9cf48ec..bce0a337 100644 --- a/app/ollama.iss +++ b/app/ollama.iss @@ -87,11 +87,11 @@ DialogFontSize=12 [Files] Source: ".\app.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}" ; Flags: ignoreversion 64bit -Source: "..\ollama.exe"; DestDir: "{app}"; Flags: ignoreversion 64bit -Source: "..\dist\windows-{#ARCH}\ollama_runners\*"; DestDir: "{app}\ollama_runners"; Flags: ignoreversion 64bit recursesubdirs +Source: "..\ollama.exe"; DestDir: "{app}\bin"; Flags: ignoreversion 64bit +Source: "..\dist\windows-{#ARCH}\lib\ollama\runners\*"; DestDir: "{app}\lib\ollama\runners"; Flags: ignoreversion 64bit recursesubdirs Source: "..\dist\ollama_welcome.ps1"; DestDir: "{app}"; Flags: ignoreversion Source: ".\assets\app.ico"; DestDir: "{app}"; Flags: ignoreversion -Source: "..\dist\windows-amd64\ollama_libs\*"; DestDir: "{app}\ollama_libs\"; Flags: ignoreversion recursesubdirs +Source: "..\dist\windows-amd64\lib\ollama\*"; DestDir: "{app}\lib\ollama\"; Flags: ignoreversion recursesubdirs [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico" @@ -99,7 +99,7 @@ Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilen Name: "{userprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico" [Run] -Filename: "{cmd}"; Parameters: "/C set PATH={app};%PATH% & ""{app}\{#MyAppExeName}"""; Flags: postinstall nowait runhidden +Filename: "{cmd}"; Parameters: "/C set PATH={app}\bin;%PATH% & ""{app}\{#MyAppExeName}"""; Flags: postinstall nowait runhidden [UninstallRun] ; Filename: "{cmd}"; Parameters: "/C ""taskkill /im ''{#MyAppExeName}'' /f /t"; Flags: runhidden @@ -134,8 +134,8 @@ SetupAppRunningError=Another Ollama installer is running.%n%nPlease cancel or fi [Registry] Root: HKCU; Subkey: "Environment"; \ - ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \ - Check: NeedsAddPath('{app}') + ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; \ + Check: NeedsAddPath('{app}\bin') [Code] diff --git a/docs/linux.md b/docs/linux.md index ec730656..3ed2bed0 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -20,13 +20,12 @@ GPU. ## Manual install -### Download the `ollama` binary +### Download the `ollama` tar file -Ollama is distributed as a self-contained binary. Download it to a directory in your PATH: +Ollama is distributed as a tar file including GPU library dependencies. ```bash -sudo curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/bin/ollama -sudo chmod +x /usr/bin/ollama +curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar -C /usr -zxf - ``` ### Adding Ollama as a startup service (recommended) @@ -96,8 +95,7 @@ curl -fsSL https://ollama.com/install.sh | sh Or by downloading the ollama binary: ```bash -sudo curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/bin/ollama -sudo chmod +x /usr/bin/ollama +curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar -C /usr -zxf - ``` ## Installing specific versions diff --git a/envconfig/config.go b/envconfig/config.go index 7f0976c0..7e45a4f5 100644 --- a/envconfig/config.go +++ b/envconfig/config.go @@ -174,7 +174,7 @@ func RunnersDir() (p string) { defer func() { if p == "" { - slog.Error("unable to locate llm runner directory. Set OLLAMA_RUNNERS_DIR to the location of 'ollama_runners'") + slog.Error("unable to locate llm runner directory. Set OLLAMA_RUNNERS_DIR to the location of 'ollama/runners'") } }() @@ -190,7 +190,7 @@ func RunnersDir() (p string) { } var paths []string - for _, root := range []string{filepath.Dir(exe), cwd} { + for _, root := range []string{filepath.Dir(exe), filepath.Join(filepath.Dir(exe), ".."), cwd} { paths = append(paths, root, filepath.Join(root, runtime.GOOS+"-"+runtime.GOARCH), @@ -200,7 +200,7 @@ func RunnersDir() (p string) { // Try a few variations to improve developer experience when building from source in the local tree for _, path := range paths { - candidate := filepath.Join(path, "ollama_runners") + candidate := filepath.Join(path, "lib", "ollama", "runners") if _, err := os.Stat(candidate); err == nil { p = candidate break diff --git a/gpu/amd_common.go b/gpu/amd_common.go index 05747208..72d204f7 100644 --- a/gpu/amd_common.go +++ b/gpu/amd_common.go @@ -54,7 +54,7 @@ func commonAMDValidateLibDir() (string, error) { // Installer payload location if we're running the installed binary exe, err := os.Executable() if err == nil { - rocmTargetDir := filepath.Join(filepath.Dir(exe), "ollama_libs") + rocmTargetDir := filepath.Join(filepath.Dir(exe), "..", "lib", "ollama") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ROCM next to ollama executable " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/amd_windows.go b/gpu/amd_windows.go index 5d25a966..a0ae7c96 100644 --- a/gpu/amd_windows.go +++ b/gpu/amd_windows.go @@ -153,7 +153,7 @@ func AMDValidateLibDir() (string, error) { // Installer payload (if we're running from some other location) localAppData := os.Getenv("LOCALAPPDATA") appDir := filepath.Join(localAppData, "Programs", "Ollama") - rocmTargetDir := filepath.Join(appDir, "ollama_libs") + rocmTargetDir := filepath.Join(appDir, "..", "lib", "ollama") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ollama installed ROCm at " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/gpu.go b/gpu/gpu.go index eb87807a..391c98a8 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -653,8 +653,8 @@ func GetDepDir() string { slog.Warn("failed to lookup working directory", "error", err) } // Scan for any of our dependeices, and pick first match - for _, root := range []string{filepath.Dir(appExe), cwd} { - libDep := "ollama_libs" + for _, root := range []string{filepath.Dir(appExe), filepath.Join(filepath.Dir(appExe), ".."), cwd} { + libDep := filepath.Join("lib", "ollama") if _, err := os.Stat(filepath.Join(root, libDep)); err == nil { return filepath.Join(root, libDep) } diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index dc9dda5a..aef03f9a 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -189,7 +189,7 @@ if [ -z "${OLLAMA_SKIP_CUDA_GENERATE}" -a -d "${CUDA_LIB_DIR}" ]; then CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} ${ARM64_DEFS} ${CMAKE_CUDA_DEFS} -DGGML_STATIC=off" BUILD_DIR="../build/linux/${ARCH}/cuda${CUDA_VARIANT}" export LLAMA_SERVER_LDFLAGS="-L${CUDA_LIB_DIR} -lcudart -lcublas -lcublasLt -lcuda" - CUDA_DIST_DIR="${CUDA_DIST_DIR:-${DIST_BASE}/ollama_libs}" + CUDA_DIST_DIR="${CUDA_DIST_DIR:-${DIST_BASE}/lib/ollama}" build install echo "Installing CUDA dependencies in ${CUDA_DIST_DIR}" @@ -213,7 +213,7 @@ if [ -z "${OLLAMA_SKIP_ONEAPI_GENERATE}" -a -d "${ONEAPI_ROOT}" ]; then CC=icx CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL=ON -DGGML_SYCL_F16=OFF" BUILD_DIR="../build/linux/${ARCH}/oneapi" - ONEAPI_DIST_DIR="${DIST_BASE}/ollama_libs" + ONEAPI_DIST_DIR="${DIST_BASE}/lib/ollama" export LLAMA_SERVER_LDFLAGS="-fsycl -lOpenCL -lmkl_core -lmkl_sycl_blas -lmkl_intel_ilp64 -lmkl_tbb_thread -ltbb" DEBUG_FLAGS="" # icx compiles with -O0 if we pass -g, so we must remove it build @@ -260,7 +260,7 @@ if [ -z "${OLLAMA_SKIP_ROCM_GENERATE}" -a -d "${ROCM_PATH}" ]; then echo "Building custom ROCM GPU" fi BUILD_DIR="../build/linux/${ARCH}/rocm${ROCM_VARIANT}" - ROCM_DIST_DIR="${DIST_BASE}/ollama_libs" + ROCM_DIST_DIR="${DIST_BASE}/lib/ollama" # TODO figure out how to disable runpath (rpath) # export CMAKE_HIP_FLAGS="-fno-rtlib-add-rpath" # doesn't work export LLAMA_SERVER_LDFLAGS="-L${ROCM_PATH}/lib -L/opt/amdgpu/lib/x86_64-linux-gnu/ -lhipblas -lrocblas -lamdhip64 -lrocsolver -lamd_comgr -lhsa-runtime64 -lrocsparse -ldrm -ldrm_amdgpu" diff --git a/llm/generate/gen_windows.ps1 b/llm/generate/gen_windows.ps1 index 42708d3e..4d43c9e2 100644 --- a/llm/generate/gen_windows.ps1 +++ b/llm/generate/gen_windows.ps1 @@ -35,7 +35,7 @@ function init_vars { ) $script:commonCpuDefs = @("-DCMAKE_POSITION_INDEPENDENT_CODE=on") $script:ARCH = $Env:PROCESSOR_ARCHITECTURE.ToLower() - $script:DIST_BASE = "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_runners" + $script:DIST_BASE = "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\runners" md "$script:DIST_BASE" -ea 0 > $null if ($env:CGO_CFLAGS -contains "-g") { $script:cmakeDefs += @("-DCMAKE_VERBOSE_MAKEFILE=on", "-DLLAMA_SERVER_VERBOSE=on", "-DCMAKE_BUILD_TYPE=RelWithDebInfo") @@ -286,11 +286,11 @@ function build_cuda() { sign install - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" -ea 0 > $null - write-host "copying CUDA dependencies to ${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${script:CUDA_LIB_DIR}\cudart64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${script:CUDA_LIB_DIR}\cublas64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${script:CUDA_LIB_DIR}\cublasLt64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" -ea 0 > $null + write-host "copying CUDA dependencies to ${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${script:CUDA_LIB_DIR}\cudart64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${script:CUDA_LIB_DIR}\cublas64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${script:CUDA_LIB_DIR}\cublasLt64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" } else { write-host "Skipping CUDA generation step" } @@ -324,17 +324,17 @@ function build_oneapi() { sign install - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" -ea 0 > $null - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libirngmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libmmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_level_zero.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_unified_runtime.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_win_proxy_loader.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\svml_dispmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\compiler\latest\bin\sycl7.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_core.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_sycl_blas.4.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_tbb_thread.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" -ea 0 > $null + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libirngmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\libmmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_level_zero.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_unified_runtime.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\pi_win_proxy_loader.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\svml_dispmd.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\compiler\latest\bin\sycl7.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_core.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_sycl_blas.4.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:ONEAPI_ROOT}\mkl\latest\bin\mkl_tbb_thread.2.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" } else { Write-Host "Skipping oneAPI generation step" } @@ -384,11 +384,11 @@ function build_rocm() { sign install - md "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\rocblas\library\" -ea 0 > $null - cp "${env:HIP_PATH}\bin\hipblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" - cp "${env:HIP_PATH}\bin\rocblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\" + md "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\rocblas\library\" -ea 0 > $null + cp "${env:HIP_PATH}\bin\hipblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" + cp "${env:HIP_PATH}\bin\rocblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\" # amdhip64.dll dependency comes from the driver and must be installed on the host to use AMD GPUs - cp "${env:HIP_PATH}\bin\rocblas\library\*" "${script:SRC_DIR}\dist\windows-${script:ARCH}\ollama_libs\rocblas\library\" + cp "${env:HIP_PATH}\bin\rocblas\library\*" "${script:SRC_DIR}\dist\windows-${script:ARCH}\lib\ollama\rocblas\library\" } else { write-host "Skipping ROCm generation step" } diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1 index 50b60230..9cebf1f4 100644 --- a/scripts/build_windows.ps1 +++ b/scripts/build_windows.ps1 @@ -122,8 +122,8 @@ function buildOllama() { /csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} ollama.exe if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)} } - New-Item -ItemType Directory -Path .\dist\windows-${script:TARGET_ARCH}\ -Force - cp .\ollama.exe .\dist\windows-${script:TARGET_ARCH}\ + New-Item -ItemType Directory -Path .\dist\windows-${script:TARGET_ARCH}\bin\ -Force + cp .\ollama.exe .\dist\windows-${script:TARGET_ARCH}\bin\ } function buildApp() { @@ -142,22 +142,22 @@ function buildApp() { function gatherDependencies() { write-host "Gathering runtime dependencies" cd "${script:SRC_DIR}" - md "${script:DEPS_DIR}\ollama_libs" -ea 0 > $null + md "${script:DEPS_DIR}\lib\ollama" -ea 0 > $null # TODO - this varies based on host build system and MSVC version - drive from dumpbin output # currently works for Win11 + MSVC 2019 + Cuda V11 - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DEPS_DIR}\ollama_libs\" - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DEPS_DIR}\ollama_libs\" - cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140_1.dll" "${script:DEPS_DIR}\ollama_libs\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\msvcp140*.dll" "${script:DEPS_DIR}\lib\ollama\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140.dll" "${script:DEPS_DIR}\lib\ollama\" + cp "${env:VCToolsRedistDir}\x64\Microsoft.VC*.CRT\vcruntime140_1.dll" "${script:DEPS_DIR}\lib\ollama\" foreach ($part in $("runtime", "stdio", "filesystem", "math", "convert", "heap", "string", "time", "locale", "environment")) { - cp "$env:VCToolsRedistDir\..\..\..\Tools\Llvm\x64\bin\api-ms-win-crt-${part}*.dll" "${script:DEPS_DIR}\ollama_libs\" + cp "$env:VCToolsRedistDir\..\..\..\Tools\Llvm\x64\bin\api-ms-win-crt-${part}*.dll" "${script:DEPS_DIR}\lib\ollama\" } cp "${script:SRC_DIR}\app\ollama_welcome.ps1" "${script:SRC_DIR}\dist\" if ("${env:KEY_CONTAINER}") { write-host "about to sign" - foreach ($file in (get-childitem "${script:DEPS_DIR}\ollama_libs\cu*.dll") + @("${script:SRC_DIR}\dist\ollama_welcome.ps1")){ + foreach ($file in (get-childitem "${script:DEPS_DIR}\lib\ollama\cu*.dll") + @("${script:SRC_DIR}\dist\ollama_welcome.ps1")){ write-host "signing $file" & "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" ` /csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} $file diff --git a/scripts/install.sh b/scripts/install.sh index f0439b00..a02a0675 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -66,7 +66,7 @@ fi for BINDIR in /usr/local/bin /usr/bin /bin; do echo $PATH | grep -q $BINDIR && break || continue done -OLLAMA_INSTALL_DIR=${OLLAMA_INSTALL_DIR:-${BINDIR}} +OLLAMA_INSTALL_DIR=$(dirname ${BINDIR}) status "Installing ollama to $OLLAMA_INSTALL_DIR" $SUDO install -o0 -g0 -m755 -d $BINDIR @@ -77,18 +77,22 @@ if curl -I --silent --fail --location "https://ollama.com/download/ollama-linux- "https://ollama.com/download/ollama-linux-${ARCH}.tgz${VER_PARAM}" | \ $SUDO tar -xzf - -C "$OLLAMA_INSTALL_DIR" BUNDLE=1 + if [ "$OLLAMA_INSTALL_DIR/bin/ollama" != "$BINDIR/ollama" ] ; then + status "Making ollama accessible in the PATH in $BINDIR" + $SUDO ln -sf "$OLLAMA_INSTALL_DIR/ollama" "$BINDIR/ollama" + fi else status "Downloading Linux ${ARCH} CLI" curl --fail --show-error --location --progress-bar -o "$TEMP_DIR/ollama"\ "https://ollama.com/download/ollama-linux-${ARCH}${VER_PARAM}" $SUDO install -o0 -g0 -m755 $TEMP_DIR/ollama $OLLAMA_INSTALL_DIR/ollama BUNDLE=0 + if [ "$OLLAMA_INSTALL_DIR/ollama" != "$BINDIR/ollama" ] ; then + status "Making ollama accessible in the PATH in $BINDIR" + $SUDO ln -sf "$OLLAMA_INSTALL_DIR/ollama" "$BINDIR/ollama" + fi fi -if [ "$OLLAMA_INSTALL_DIR/ollama" != "$BINDIR/ollama" ] ; then - status "Making ollama accessible in the PATH in $BINDIR" - $SUDO ln -sf "$OLLAMA_INSTALL_DIR/ollama" "$BINDIR/ollama" -fi install_success() { status 'The Ollama API is now available at 127.0.0.1:11434.' From f9e31da9463092d7b3661594788c259d6d55b3d9 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 15 Aug 2024 14:38:14 -0700 Subject: [PATCH 055/174] Review comments --- .github/workflows/release.yaml | 106 ++++++--------------------------- docs/linux.md | 8 +-- gpu/cuda_common.go | 2 +- gpu/gpu.go | 16 ++--- llm/generate/gen_windows.ps1 | 4 +- 5 files changed, 32 insertions(+), 104 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4bd68455..508fbb35 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -183,10 +183,17 @@ jobs: name: windows-rocm-deps path: dist/deps/* - # CUDA v11 generation step - generate-windows-cuda-v11: + # CUDA generation step + generate-windows-cuda: environment: release runs-on: windows + strategy: + matrix: + cuda: + - version: "11" + url: 'https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.89_win10.exe' + - version: "12" + url: 'https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_551.61_windows.exe' env: KEY_CONTAINER: ${{ vars.KEY_CONTAINER }} steps: @@ -220,11 +227,11 @@ jobs: with: go-version-file: go.mod cache: true - - name: 'Install CUDA' + - name: 'Install CUDA ${{ matrix.cuda.version }}' run: | $ErrorActionPreference = "Stop" write-host "downloading CUDA Installer" - Invoke-WebRequest -Uri "https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.89_win10.exe" -OutFile "${env:RUNNER_TEMP}\cuda-install.exe" + Invoke-WebRequest -Uri "${{ matrix.cuda.url }}" -OutFile "${env:RUNNER_TEMP}\cuda-install.exe" write-host "Installing CUDA" Start-Process "${env:RUNNER_TEMP}\cuda-install.exe" -ArgumentList '-s' -NoNewWindow -Wait write-host "Completed CUDA" @@ -256,7 +263,7 @@ jobs: cp "${NVIDIA_DIR}\cublasLt64_*.dll" "dist\deps\" - uses: actions/upload-artifact@v4 with: - name: generate-windows-cuda-v11 + name: generate-windows-cuda-${{ matrix.cuda.version }} path: | llm/build/**/bin/* dist/windows-amd64/** @@ -265,95 +272,13 @@ jobs: name: windows-cuda-deps path: dist/deps/* - # CUDA v12 generation step - generate-windows-cuda-v12: - environment: release - runs-on: windows - env: - KEY_CONTAINER: ${{ vars.KEY_CONTAINER }} - steps: - - uses: actions/checkout@v4 - - name: Set Version - shell: bash - run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV - - uses: 'google-github-actions/auth@v2' - with: - project_id: 'ollama' - credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}' - - run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt - - name: install Windows SDK 8.1 to get signtool - run: | - $ErrorActionPreference = "Stop" - write-host "downloading SDK" - Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe" - Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait - write-host "Win SDK 8.1 installed" - gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe' - - name: install signing plugin - run: | - $ErrorActionPreference = "Stop" - write-host "downloading plugin" - Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip" - Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\ - write-host "Installing plugin" - & "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet - write-host "plugin installed" - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - name: 'Install CUDA' - run: | - $ErrorActionPreference = "Stop" - write-host "downloading CUDA Installer" - Invoke-WebRequest -Uri "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_551.61_windows.exe" -OutFile "${env:RUNNER_TEMP}\cuda-install.exe" - write-host "Installing CUDA" - Start-Process "${env:RUNNER_TEMP}\cuda-install.exe" -ArgumentList '-s' -NoNewWindow -Wait - write-host "Completed CUDA" - $cudaPath=((resolve-path "c:\Program Files\NVIDIA*\CUDA\v*\bin\nvcc.exe")[0].path | split-path | split-path) - $cudaVer=($cudaPath | split-path -leaf ) -replace 'v(\d+).(\d+)', '$1_$2' - echo "$cudaPath\bin" >> $env:GITHUB_PATH - echo "CUDA_PATH=$cudaPath" >> $env:GITHUB_ENV - echo "CUDA_PATH_V${cudaVer}=$cudaPath" >> $env:GITHUB_ENV - echo "CUDA_PATH_VX_Y=CUDA_PATH_V${cudaVer}" >> $env:GITHUB_ENV - - name: 'Verify CUDA' - run: nvcc -V - - run: go get ./... - - name: go generate - run: | - $gopath=(get-command go).source | split-path -parent - $cudabin=(get-command nvcc).source | split-path - & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1" - cd $env:GITHUB_WORKSPACE - $env:CMAKE_SYSTEM_VERSION="10.0.22621.0" - $env:PATH="$gopath;$cudabin;$env:PATH" - $env:OLLAMA_SKIP_CPU_GENERATE="1" - go generate -x ./... - - name: 'gather cuda dependencies' - run: | - $NVIDIA_DIR=(resolve-path 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\*\bin\')[0] - md "dist\deps" - cp "${NVIDIA_DIR}\cudart64_*.dll" "dist\deps\" - cp "${NVIDIA_DIR}\cublas64_*.dll" "dist\deps\" - cp "${NVIDIA_DIR}\cublasLt64_*.dll" "dist\deps\" - - uses: actions/upload-artifact@v4 - with: - name: generate-windows-cuda-v12 - path: | - llm/build/**/bin/* - dist/windows-amd64/** - - uses: actions/upload-artifact@v4 - with: - name: windows-cuda-deps - path: dist/deps/* # Import the prior generation steps and build the final windows assets build-windows: environment: release runs-on: windows needs: - - generate-windows-cuda-v11 - - generate-windows-cuda-v12 + - generate-windows-cuda - generate-windows-rocm - generate-windows-cpu env: @@ -397,7 +322,10 @@ jobs: name: generate-windows-cpu - uses: actions/download-artifact@v4 with: - name: generate-windows-cuda-v11 + name: generate-windows-cuda-11 + - uses: actions/download-artifact@v4 + with: + name: generate-windows-cuda-12 - uses: actions/download-artifact@v4 with: name: windows-cuda-deps diff --git a/docs/linux.md b/docs/linux.md index 3ed2bed0..d1d5892c 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -20,12 +20,12 @@ GPU. ## Manual install -### Download the `ollama` tar file +### Download `ollama` -Ollama is distributed as a tar file including GPU library dependencies. +Download and extract the Linux package: ```bash -curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar -C /usr -zxf - +curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar zx -C /usr ``` ### Adding Ollama as a startup service (recommended) @@ -95,7 +95,7 @@ curl -fsSL https://ollama.com/install.sh | sh Or by downloading the ollama binary: ```bash -curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar -C /usr -zxf - +curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar zx -C /usr ``` ## Installing specific versions diff --git a/gpu/cuda_common.go b/gpu/cuda_common.go index defaa60a..827cc9b4 100644 --- a/gpu/cuda_common.go +++ b/gpu/cuda_common.go @@ -28,7 +28,7 @@ func cudaGetVisibleDevicesEnv(gpuInfo []GpuInfo) (string, string) { return "CUDA_VISIBLE_DEVICES", strings.Join(ids, ",") } -func cudaGetVariant(gpuInfo CudaGPUInfo) string { +func cudaVariant(gpuInfo CudaGPUInfo) string { if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" { if CudaTegra != "" { ver := strings.Split(CudaTegra, ".") diff --git a/gpu/gpu.go b/gpu/gpu.go index 391c98a8..72d237a6 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -225,7 +225,7 @@ func GetGPUInfo() GpuInfoList { return GpuInfoList{cpus[0].GpuInfo} } - depPath := GetDepDir() + depPath := LibraryDir() // Load ALL libraries cHandles = initCudaHandles() @@ -264,20 +264,20 @@ func GetGPUInfo() GpuInfoList { gpuInfo.computeMajor = int(memInfo.major) gpuInfo.computeMinor = int(memInfo.minor) gpuInfo.MinimumMemory = cudaMinimumMemory - cudaVariant := cudaGetVariant(gpuInfo) + variant := cudaVariant(gpuInfo) if depPath != "" { gpuInfo.DependencyPath = depPath // Check for variant specific directory - if cudaVariant != "" { - if _, err := os.Stat(filepath.Join(depPath, "cuda_"+cudaVariant)); err == nil { - gpuInfo.DependencyPath = filepath.Join(depPath, "cuda_"+cudaVariant) + if variant != "" { + if _, err := os.Stat(filepath.Join(depPath, "cuda_"+variant)); err == nil { + gpuInfo.DependencyPath = filepath.Join(depPath, "cuda_"+variant) } } } gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) gpuInfo.DriverMajor = driverMajor gpuInfo.DriverMinor = driverMinor - gpuInfo.Variant = cudaGetVariant(gpuInfo) + gpuInfo.Variant = variant // query the management library as well so we can record any skew between the two // which represents overhead on the GPU we must set aside on subsequent updates @@ -468,7 +468,7 @@ func FindGPULibs(baseLibName string, defaultPatterns []string) []string { slog.Debug("Searching for GPU library", "name", baseLibName) // Start with our bundled libraries - patterns := []string{filepath.Join(GetDepDir(), baseLibName)} + patterns := []string{filepath.Join(LibraryDir(), baseLibName)} switch runtime.GOOS { case "windows": @@ -642,7 +642,7 @@ func (l GpuInfoList) GetVisibleDevicesEnv() (string, string) { } } -func GetDepDir() string { +func LibraryDir() string { // On Windows/linux we bundle the dependencies at the same level as the executable appExe, err := os.Executable() if err != nil { diff --git a/llm/generate/gen_windows.ps1 b/llm/generate/gen_windows.ps1 index 4d43c9e2..cbdfd09f 100644 --- a/llm/generate/gen_windows.ps1 +++ b/llm/generate/gen_windows.ps1 @@ -117,7 +117,7 @@ function build { if ($cmakeDefs -contains "-G") { $extra=@("-j8") } else { - $extra= @("--", "/p:CL_MPcount=8") + $extra= @("--", "/maxCpuCount:8") } write-host "building with: cmake --build $script:buildDir --config $script:config $($script:cmakeTargets | ForEach-Object { `"--target`", $_ }) $extra" & cmake --build $script:buildDir --config $script:config ($script:cmakeTargets | ForEach-Object { "--target", $_ }) $extra @@ -273,7 +273,7 @@ function build_cuda() { "-DGGML_CUDA=ON", "-DGGML_AVX=on", "-DGGML_AVX2=off", - "-DCMAKE_CUDA_FLAGS=-t8", + "-DCMAKE_CUDA_FLAGS=-t6", "-DCMAKE_CUDA_ARCHITECTURES=${script:CMAKE_CUDA_ARCHITECTURES}", "-DCMAKE_CUDA_COMPILER_TOOLKIT_ROOT=$env:CUDA_PATH" ) From d8be22e47d460d1483846e2effb9b67fbfce1c0b Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Mon, 19 Aug 2024 12:07:18 -0700 Subject: [PATCH 056/174] Fix overlapping artifact name on CI --- .github/workflows/release.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 508fbb35..f6489dac 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -269,7 +269,7 @@ jobs: dist/windows-amd64/** - uses: actions/upload-artifact@v4 with: - name: windows-cuda-deps + name: windows-cuda-deps-${{ matrix.cuda.version }} path: dist/deps/* @@ -328,7 +328,10 @@ jobs: name: generate-windows-cuda-12 - uses: actions/download-artifact@v4 with: - name: windows-cuda-deps + name: windows-cuda-deps-11 + - uses: actions/download-artifact@v4 + with: + name: windows-cuda-deps-12 - uses: actions/download-artifact@v4 with: name: windows-rocm-deps From f91c9e370923d3b10a88732ab577e2728022152d Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Mon, 19 Aug 2024 13:48:45 -0700 Subject: [PATCH 057/174] CI: handle directories during checksum (#6427) --- .github/workflows/release.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f6489dac..aad49d98 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -472,7 +472,8 @@ jobs: merge-multiple: true - run: | ls -lh dist/ - (cd dist; sha256sum * > sha256sum.txt) + (cd dist; find . -type f | xargs sha256sum > ../sha256sum.txt) + mv sha256sum.txt dist/ cat dist/sha256sum.txt - name: Create or update Release run: | From 19e5a890f70b95a55c9de6a55357d78fc0a4ff81 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Mon, 19 Aug 2024 15:19:21 -0700 Subject: [PATCH 058/174] CI: remove directories from dist dir before upload step (#6429) --- .github/workflows/release.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index aad49d98..2cf4d2c2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -474,6 +474,7 @@ jobs: ls -lh dist/ (cd dist; find . -type f | xargs sha256sum > ../sha256sum.txt) mv sha256sum.txt dist/ + mv dist/linux-???64 . cat dist/sha256sum.txt - name: Create or update Release run: | From a017cf2fea4aaa376087520382058c42cffce097 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 20 Aug 2024 07:26:38 -0700 Subject: [PATCH 059/174] Split rocm back out of bundle (#6432) We're over budget for github's maximum release artifact size with rocm + 2 cuda versions. This splits rocm back out as a discrete artifact, but keeps the layout so it can be extracted into the same location as the main bundle. --- .github/workflows/release.yaml | 1 + Dockerfile | 4 ++-- llm/generate/gen_linux.sh | 3 ++- scripts/build_linux.sh | 6 ++++++ scripts/install.sh | 5 +++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2cf4d2c2..9c1e3e13 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -475,6 +475,7 @@ jobs: (cd dist; find . -type f | xargs sha256sum > ../sha256sum.txt) mv sha256sum.txt dist/ mv dist/linux-???64 . + mv dist/linux-amd64-rocm . cat dist/sha256sum.txt - name: Create or update Release run: | diff --git a/Dockerfile b/Dockerfile index d4b86918..c46477b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -95,8 +95,8 @@ ARG AMDGPU_TARGETS ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh -RUN mkdir -p ../../dist/linux-amd64/lib/ollama && \ - (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64/lib/ollama && tar xf - ) +RUN mkdir -p ../../dist/linux-amd64-rocm/lib/ollama && \ + (cd /opt/rocm/lib && tar cf - rocblas/library) | (cd ../../dist/linux-amd64-rocm/lib/ollama && tar xf - ) FROM --platform=linux/amd64 centos:7 AS cpu-builder-amd64 ARG CMAKE_VERSION diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index aef03f9a..6927dda8 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -260,7 +260,8 @@ if [ -z "${OLLAMA_SKIP_ROCM_GENERATE}" -a -d "${ROCM_PATH}" ]; then echo "Building custom ROCM GPU" fi BUILD_DIR="../build/linux/${ARCH}/rocm${ROCM_VARIANT}" - ROCM_DIST_DIR="${DIST_BASE}/lib/ollama" + # ROCm dependencies are too large to fit into a unified bundle + ROCM_DIST_DIR="${DIST_BASE}/../linux-${GOARCH}-rocm/lib/ollama" # TODO figure out how to disable runpath (rpath) # export CMAKE_HIP_FLAGS="-fno-rtlib-add-rpath" # doesn't work export LLAMA_SERVER_LDFLAGS="-L${ROCM_PATH}/lib -L/opt/amdgpu/lib/x86_64-linux-gnu/ -lhipblas -lrocblas -lamdhip64 -lrocsolver -lamd_comgr -lhsa-runtime64 -lrocsparse -ldrm -ldrm_amdgpu" diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh index adda2ad7..6cb0d0cd 100755 --- a/scripts/build_linux.sh +++ b/scripts/build_linux.sh @@ -24,8 +24,14 @@ for TARGETARCH in ${BUILD_ARCH}; do docker create --platform linux/$TARGETARCH --name builder-$TARGETARCH builder:$TARGETARCH rm -rf ./dist/linux-$TARGETARCH docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/dist/linux-$TARGETARCH ./dist + if echo ${TARGETARCH} | grep "amd64" > /dev/null; then + docker cp builder-$TARGETARCH:/go/src/github.com/ollama/ollama/dist/linux-$TARGETARCH-rocm ./dist + fi docker rm builder-$TARGETARCH echo "Compressing final linux bundle..." rm -f ./dist/ollama-linux-$TARGETARCH.tgz (cd dist/linux-$TARGETARCH && tar cf - . | ${GZIP} --best > ../ollama-linux-$TARGETARCH.tgz ) + if [ -d dist/linux-$TARGETARCH-rocm ]; then + (cd dist/linux-$TARGETARCH-rocm && tar cf - . | ${GZIP} --best > ../ollama-linux-$TARGETARCH-rocm.tgz ) + fi done diff --git a/scripts/install.sh b/scripts/install.sh index a02a0675..25f57565 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -199,6 +199,11 @@ fi if check_gpu lspci amdgpu || check_gpu lshw amdgpu; then if [ $BUNDLE -ne 0 ]; then + status "Downloading Linux ROCm ${ARCH} bundle" + curl --fail --show-error --location --progress-bar \ + "https://ollama.com/download/ollama-linux-${ARCH}-rocm.tgz${VER_PARAM}" | \ + $SUDO tar -xzf - -C "$OLLAMA_INSTALL_DIR" + install_success status "AMD GPU ready." exit 0 From 5a28b9cf5fcb3994aa1a143118c73c7d1fbf3bf9 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 6 Jun 2024 08:59:04 -0700 Subject: [PATCH 060/174] bert --- convert/convert.go | 12 ++ convert/convert_bert.go | 176 +++++++++++++++++++++++++ convert/convert_test.go | 1 + convert/reader.go | 2 + convert/testdata/all-MiniLM-L6-v2.json | 124 +++++++++++++++++ convert/tokenizer.go | 31 ++--- 6 files changed, 331 insertions(+), 15 deletions(-) create mode 100644 convert/convert_bert.go create mode 100644 convert/testdata/all-MiniLM-L6-v2.json diff --git a/convert/convert.go b/convert/convert.go index 24c19aa4..f51e9665 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -66,6 +66,10 @@ type Converter interface { writeFile(io.WriteSeeker, llm.KV, []llm.Tensor) error } +type moreParser interface { + parseMore(fs.FS) error +} + // Convert writes an Ollama compatible model to the provided io.WriteSeeker based on configurations // and files it finds in the input path. // Supported input model formats include safetensors. @@ -95,6 +99,8 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { conv = &gemma{} case "Phi3ForCausalLM": conv = &phi3{} + case "BertModel": + conv = &bert{} default: return errors.New("unsupported architecture") } @@ -103,6 +109,12 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { return err } + if t, ok := conv.(moreParser); ok { + if err := t.parseMore(fsys); err != nil { + return err + } + } + t, err := parseTokenizer(fsys, conv.specialTokenTypes()) if err != nil { return err diff --git a/convert/convert_bert.go b/convert/convert_bert.go new file mode 100644 index 00000000..62fad147 --- /dev/null +++ b/convert/convert_bert.go @@ -0,0 +1,176 @@ +package convert + +import ( + "cmp" + "encoding/json" + "io/fs" + "path/filepath" + "slices" + "strings" + + "github.com/ollama/ollama/llm" +) + +type bert struct { + Parameters + NLayers uint32 `json:"n_layers"` + NumHiddenLayers uint32 `json:"num_hidden_layers"` + NLayer uint32 `json:"n_layer"` + MaxPositionEmbeddings uint32 `json:"max_position_embeddings"` + NCtx uint32 `json:"n_ctx"` + HiddenSize uint32 `json:"hidden_size"` + NEmbd uint32 `json:"n_embd"` + IntermediateSize uint32 `json:"intermediate_size"` + NInner uint32 `json:"n_inner"` + NumAttentionHeads uint32 `json:"num_attention_heads"` + NHead uint32 `json:"n_head"` + NumKeyValueHeads uint32 `json:"num_key_value_heads"` + LayerNormEPS float32 `json:"layer_norm_eps"` + LayerNormEpsilon float32 `json:"layer_norm_epsilon"` + NormEpsilon float32 `json:"norm_epsilon"` + + PoolingType uint32 +} + +var ( + _ Converter = (*bert)(nil) + _ moreParser = (*bert)(nil) +) + +func (p *bert) parseMore(fsys fs.FS) error { + bts, err := fs.ReadFile(fsys, "modules.json") + if err != nil { + return err + } + + var modules []struct { + Type string `json:"type"` + Path string `json:"path"` + } + + if err := json.Unmarshal(bts, &modules); err != nil { + return err + } + + var pooling string + for _, m := range modules { + if m.Type == "sentence_transformers.models.Pooling" { + pooling = m.Path + break + } + } + + if pooling != "" { + bts, err := fs.ReadFile(fsys, filepath.Join(pooling, "config.json")) + if err != nil { + return err + } + + var pc struct { + PoolingModeCLSToken bool `json:"pooling_mode_cls_token"` + PoolingModeMeanTokens bool `json:"pooling_mode_mean_tokens"` + } + + if err := json.Unmarshal(bts, &pc); err != nil { + return err + } + + if pc.PoolingModeMeanTokens { + p.PoolingType = 1 + } else if pc.PoolingModeCLSToken { + p.PoolingType = 2 + } + } + + return nil +} + +func (p *bert) KV(t *Tokenizer) llm.KV { + kv := p.Parameters.KV(t) + kv["general.architecture"] = "bert" + kv["general.name"] = "bert" + kv["bert.attention.causal"] = false + kv["bert.pooling_type"] = p.PoolingType + + kv["bert.block_count"] = cmp.Or(p.NLayers, p.NumHiddenLayers, p.NLayer) + + if contextLength := cmp.Or(p.MaxPositionEmbeddings, p.NCtx); contextLength > 0 { + kv["bert.context_length"] = contextLength + } + + if embeddingLength := cmp.Or(p.HiddenSize, p.NEmbd); embeddingLength > 0 { + kv["bert.embedding_length"] = cmp.Or(p.HiddenSize, p.NEmbd) + } + + if feedForwardLength := cmp.Or(p.IntermediateSize, p.NInner); feedForwardLength > 0 { + kv["bert.feed_forward_length"] = cmp.Or(p.IntermediateSize, p.NInner) + } + + if headCount := cmp.Or(p.NumAttentionHeads, p.NHead); headCount > 0 { + kv["bert.attention.head_count"] = cmp.Or(p.NumAttentionHeads, p.NHead) + } + + if layerNormEpsilon := cmp.Or(p.LayerNormEPS, p.LayerNormEpsilon, p.NormEpsilon); layerNormEpsilon > 0 { + kv["bert.attention.layer_norm_epsilon"] = layerNormEpsilon + } + + kv["tokenizer.ggml.model"] = "bert" + kv["tokenizer.ggml.token_type_count"] = uint32(2) + + // convert to phantom space tokens + for i, e := range t.Tokens { + if strings.HasPrefix(e, "[") && strings.HasSuffix(e, "]") { + // noop + } else if strings.HasPrefix(e, "##") { + t.Tokens[i] = e[2:] + } else { + t.Tokens[i] = "\u2581" + e + } + } + + kv["tokenizer.ggml.tokens"] = t.Tokens + + return kv +} + +func (p *bert) Tensors(ts []Tensor) []llm.Tensor { + var out []llm.Tensor + for _, t := range ts { + if slices.Contains([]string{ + "embeddings.position_ids", + "pooler.dense.weight", + "pooler.dense.bias", + }, t.Name()) { + continue + } + + name := p.tensorName(t.Name()) + out = append(out, llm.Tensor{ + Name: name, + Kind: t.Kind(), + Shape: t.Shape(), + WriterTo: t, + }) + } + + return out +} + +func (bert) tensorName(n string) string { + return strings.NewReplacer( + "encoder.layer", "blk", + "encoder.layers", "blk", + "embeddings.word_embeddings", "token_embd", + "embeddings.token_type_embeddings", "token_types", + "embeddings.LayerNorm", "token_embd_norm", + "embeddings.position_embeddings", "position_embd", + "attention.self.query", "attn_q", + "attention.self.key", "attn_k", + "attention.self.value", "attn_v", + "attention.output.dense", "attn_output", + "attention.output.LayerNorm", "attn_output_norm", + "intermediate.dense", "ffn_up", + "output.dense", "ffn_down", + "output.LayerNorm", "layer_output_norm", + ).Replace(n) +} diff --git a/convert/convert_test.go b/convert/convert_test.go index cb2c585e..e3ab0098 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -67,6 +67,7 @@ func TestConvertFull(t *testing.T) { "gemma-2b-it", // microsoft/Phi-3-mini-128-instruct@d548c233192db00165d842bf8edff054bb3212f8 "Phi-3-mini-128k-instruct", + "all-MiniLM-L6-v2", } for i := range cases { diff --git a/convert/reader.go b/convert/reader.go index ce95208e..294a7c40 100644 --- a/convert/reader.go +++ b/convert/reader.go @@ -37,6 +37,8 @@ const ( func (t tensorBase) Kind() uint32 { if strings.HasSuffix(t.name, ".block_sparse_moe.gate.weight") { return 0 + } else if t.name == "embeddings.token_type_embeddings.weight" { + return 0 } switch len(t.shape) { diff --git a/convert/testdata/all-MiniLM-L6-v2.json b/convert/testdata/all-MiniLM-L6-v2.json new file mode 100644 index 00000000..15c8f039 --- /dev/null +++ b/convert/testdata/all-MiniLM-L6-v2.json @@ -0,0 +1,124 @@ +{ + "general.architecture": "bert", + "general.file_type": "1", + "general.quantization_version": "2", + "bert.attention.causal": "false", + "bert.attention.head_count": "12", + "bert.attention.layer_norm_epsilon": "1e-12", + "bert.block_count": "6", + "bert.context_length": "512", + "bert.embedding_length": "384", + "bert.feed_forward_length": "1536", + "bert.pooling_type": "1", + "tokenizer.ggml.model": "bert", + "tokenizer.ggml.padding_token_id": "0", + "tokenizer.ggml.unknown_token_id": "100", + "tokenizer.ggml.cls_token_id": "101", + "tokenizer.ggml.seperator_token_id": "102", + "tokenizer.ggml.mask_token_id": "103", + "tokenizer.ggml.token_type_count": "2", + "tokenizer.ggml.scores": "6db964fe67338aca57790481a390121ff3dd643eebe49f7dd308029ad99abb6f", + "tokenizer.ggml.token_type": "98d247c5404b6b18f05f133b92dd56edf6efefefac326794b00d7b351f6c5aa1", + "tokenizer.ggml.tokens": "9efe405e229a45ff9916f54c475d151d2200cd2ab0006f347abfb069cf096c86", + "token_embd.weight": "8c1ee80a9ea4f65aa385ba30112010068af3d209bebc6e149d3d4589c2cd0a5a", + "position_embd.weight": "6c516f0b1c4e2388ab90394dd80ad69e4e4509b890982fc3408108ae66210eb6", + "token_types.weight": "f879f8e422ed211948f28b560d3c5e17aae7993f063b51196a28cf5c0fb3da21", + "token_embd_norm.weight": "75076e095d717aab96f8b6beeee503c27940d9a76f2b891a0e3de72f8a6043e4", + "token_embd_norm.bias": "298735285ffe944e1bf03e5d35c7280326b85cf121bde9874f1af5dc51ab939d", + "blk.0.attn_q.weight": "ab0923ce4c1549175112dcdfcc860fe30137f991e03ea6857fb5993670adaf6c", + "blk.0.attn_q.bias": "a3ec29551dabf976e1d34256b8ab5ab7b758f3ed9742c3cafdbd984d5441df62", + "blk.0.attn_k.weight": "4c1038a6d035c3e9ffed7fa672b614627814752503755fbad0cfb76a41ad71ba", + "blk.0.attn_k.bias": "e0363930eb588d91816aa3d230bb03b6e2551c165117b80b8d60397413819ef9", + "blk.0.attn_v.weight": "425e2e53e3f00ce98d29c3e6a161eb55d3e6ae0d96fdb9f6242d1c4fd6eef4b3", + "blk.0.attn_v.bias": "6579173a1e65ee124fbd0bd53cbdca4225515b4f2c5f18fb1bfd000f5978f9bb", + "blk.0.attn_output.weight": "a6d70a08cd7164de5d12af65d86d657c3db35aaecde778b2b3fda9193c4c9802", + "blk.0.attn_output.bias": "2b8d12c4f9a9c5bfaa29c597839568f6e0525cb41eeaf64ddeb6bd84dfeb9701", + "blk.0.attn_output_norm.weight": "bbe6e502a473228b525aeed26cc31b7db123ad63bdc5a6eebac6ea70b8b51d62", + "blk.0.attn_output_norm.bias": "36eaacaf0007c5c62daea97aab0115390c0682914f78482e37eb76885f4b7a50", + "blk.0.ffn_up.weight": "24654561c76ce387d125759ba843f06b904ef721fcceaeff6ccc62180a48e874", + "blk.0.ffn_up.bias": "fd3f0126aa1d95768fa60eb6f4ab8a2763cfcb7e5405f35b92353031d86f4d34", + "blk.0.ffn_down.weight": "97a829763a6a5bf3329ceb4d39c424ba4787d61653a5b0bbd1f84782e4d4e0ca", + "blk.0.ffn_down.bias": "7aa980c30ae8b4ee7f69df28808dbf5c431f56ccc4a80340f644a0419f16c054", + "blk.0.layer_output_norm.weight": "ef30dad4c2a083ae1ff5039a2a6cda60ecc89bf1e486a6f8c0d15f50589603f8", + "blk.0.layer_output_norm.bias": "8b1b77e67568b1bce43fc476de1b177c53ff688d66beb66995e8eb3dc290da8a", + "blk.1.attn_q.weight": "284331622a1f6f9b87ccee4f652bd66a394ca493c4d93be4d1844e4f6159ad10", + "blk.1.attn_q.bias": "e24ebd4860330e08f6bfdd077a82db0bee33f4c8846cf1db26327a34754c7069", + "blk.1.attn_k.weight": "729dd0d555544b5bd0f7580b3c8b384256b974605f0e7487b95f295aa032997d", + "blk.1.attn_k.bias": "2aa51a828a858f35473f54477583fea54ce2ccc34ea60fbd1d228fbe9bca827f", + "blk.1.attn_v.weight": "6be304671cc311d5ca5c103f2b51467ee800c589bc5b8101e09ff5aed1f68c21", + "blk.1.attn_v.bias": "43bcbab78a8819e07f723bc9e5b737b71e87a7594f15234e882b63e327a64199", + "blk.1.attn_output.weight": "15ec8a1a12b26c9976445308a09f748ab0e4bef0f583d13ab08c3129f8738d73", + "blk.1.attn_output.bias": "dac2146f4baa6ed16f6c0dc7443831fb7ec79bedcceafd80d1a4b628a1bb072d", + "blk.1.attn_output_norm.weight": "d2151eb33bffac536787a4c9a5d2b31c7a80b17c4611877842a3cce2cd6e98d8", + "blk.1.attn_output_norm.bias": "31e1b779716dafb855d2cf5631ee168a0ccf372eb9c6ea6091f66fa97a9b9d2d", + "blk.1.ffn_up.weight": "a57547fc3fc3b77406f5cdcb0c87af9bc184701f175c39c1f35297826fce3cc7", + "blk.1.ffn_up.bias": "123be6d541d086202913c75d878c54d59a749f3af7b58f7ef9eb9e7c62a24c9a", + "blk.1.ffn_down.weight": "cfdb79788377e5cbded8790cd41b9e66c397ecab75474071fcd7cf32d30f9613", + "blk.1.ffn_down.bias": "bcb58315519a573097960891c9ae41cf4c685ab78c3e0e77471471758a7eae88", + "blk.1.layer_output_norm.weight": "819b554271452bfb1d84c2603b90377b2e41a0ac1e3aa8b417ccf9dce63375bd", + "blk.1.layer_output_norm.bias": "47a3433ac27f5ce8947fb38dd491f3706df4ef6adb0ddf74612bf0f54b19e164", + "blk.2.attn_q.weight": "1557a9ea852b1880551f7290e00aded4f35e6c4180fdcbed1b0039bf805f639e", + "blk.2.attn_q.bias": "c3bfe5f3066f655fd36b055530997b59ff33ef013563aaeb3cb8ff07dabd59a9", + "blk.2.attn_k.weight": "cfd08eb69c61ae2f9f14f9b7ff5c5394ca264b1a9f3d48156677f90dd1766289", + "blk.2.attn_k.bias": "9b839bc0e79974a0b3f5d1895972bc6f5c9a1bc16052e1af786e6a530758152d", + "blk.2.attn_v.weight": "02b26b1208480eaeeb00e7b4cf8b690006ca14759357fc44ed4a2a8924ead993", + "blk.2.attn_v.bias": "e7e6f0089fded1659a867ab736c220d9653ea7da6b1b94baf5c8d30a748b63ab", + "blk.2.attn_output.weight": "a1db121c7d33806b349cadd050300a57db49fdc91224fd07c9ac43bf4299dc79", + "blk.2.attn_output.bias": "7675128b6a92555cd955c820311e91e9417d31f48848f45d047b4100c62148b3", + "blk.2.attn_output_norm.weight": "5b4595e0fbcba67a700c4331adf746d2fba3546364a4db5607ae241947bb1a21", + "blk.2.attn_output_norm.bias": "7b8e16826ea30e5a2ba0b02e0095a901775981a296e98819625320e983060d08", + "blk.2.ffn_up.weight": "a0d815d946ac07a65095c4ae4df77b818845e6d97795c7d82f55e689d944db59", + "blk.2.ffn_up.bias": "ce37c0a4174d6bf773ded7bd016ede627ad3bdb8bc99b9992a18dc8e8898f252", + "blk.2.ffn_down.weight": "f6231d2a25426fbd45b9f1160aa484220eb227ceef0348c4a6a6de890606e5ef", + "blk.2.ffn_down.bias": "429e00556e8dc63a785238b309b9d83738500c1ef6d736fe6526ad88ea496d27", + "blk.2.layer_output_norm.weight": "651457a573adf3f7dd9ee5dfe1c8e89389e94443993aab77ec6a0b05aa621e35", + "blk.2.layer_output_norm.bias": "41fbbeda7fd89b0cef5f945ae44011c316982390401d6f75ba8c6d365e185247", + "blk.3.attn_q.weight": "95a43f32949d2cb8d22815bb27a44abfc6665ba96221af817dfe058cb6ca72c6", + "blk.3.attn_q.bias": "f4e34385e75d8108b6b3bd336106e2133a8c9be0cc343dfe5dc48c32a823c7cb", + "blk.3.attn_k.weight": "6b892da6a17d4d3265265a15f695864a31813ee8c8e710ae9bc9e1adbc6c9a18", + "blk.3.attn_k.bias": "40b8067b641a56014cee42548240aa8930820958b1933004892b5f04fbaef39e", + "blk.3.attn_v.weight": "9fcd5922319dd2a461082a5ce040c1dfe65d87d70ca6547dd0b46eeecc3eeb2b", + "blk.3.attn_v.bias": "b528c56212e66931fdbe267ac327a9c2f87cd03baff3ea719e30afe681da15f1", + "blk.3.attn_output.weight": "e3b178c1b03981e75510e0d277af23ea59cc404b5394e61bd32291825719b502", + "blk.3.attn_output.bias": "712c84d39a6a5a9c06a09da8fd9939ba0d5525524a4bba61ea4de09b48f45cae", + "blk.3.attn_output_norm.weight": "d1ffac88e675592ff72f8a617be32b4a381d443b2f8f2645dbe44a1e5745aac0", + "blk.3.attn_output_norm.bias": "ea31a1c73146234c50e0e43f485c458413714867b8e2703af66482f7db2d6c40", + "blk.3.ffn_up.weight": "4ef4f3b9a1ea6ab2ef2eb6e8b008e06a44790d099d97482a05a51e39a29afac0", + "blk.3.ffn_up.bias": "06a4296dda16f452675c51f108079fe7722552d6521c737d97734943818b9a2b", + "blk.3.ffn_down.weight": "f114b2bebe392c7d80433bb880c6730293aa4561b0b0370dcdaf7472daebd847", + "blk.3.ffn_down.bias": "2c8e67831d28a3bf613fc7912ae3259b63d72abcaf4d30efd8800758400158de", + "blk.3.layer_output_norm.weight": "a1dfeb7b5a51dd56447312ca41e2ad2f361a3ea12ddc355127f5f4219fb0a482", + "blk.3.layer_output_norm.bias": "1ed630021b25c6c6fc93fd32988b9907df966d4982a93081f639aac3044618ab", + "blk.4.attn_q.weight": "b5fae4c1f9a5f33a2a2e816ac0c01c25f422e4efdd59ef1ed93da2610e5370fc", + "blk.4.attn_q.bias": "c2e376524ea98ac3b10d9eee19ecb1b1e261fa5149efe0232844c923dfb428fb", + "blk.4.attn_k.weight": "a4632f5ebf9321d9d08f9112a4e5dda2efe5671df4a4e67fee24845f5b14af16", + "blk.4.attn_k.bias": "a9a02ffb8b8b4f6dfe487a7e0341f1d5318c9d2b793a688f34cb1b22fc66ef60", + "blk.4.attn_v.weight": "10ad8deb81d9fa093b1e5c0f24ea82aa7df43e6aca49e260fcbea56eab8cc86a", + "blk.4.attn_v.bias": "7326813e181e021130bd33ac136293fcffccce2d1d8cb59041e5b13a8cceacf6", + "blk.4.attn_output.weight": "c92573088c7437c2b3cda51490e152c27fb19e5468df591eabba5a49d5398d44", + "blk.4.attn_output.bias": "14e10b419e5859af1eb685af5c330aee67048cd704dcead9217840c6f5393222", + "blk.4.attn_output_norm.weight": "02b6831c0e0fb0edbc579a92812a1dd972cb15d14fcd382d4427c5a7b300ac44", + "blk.4.attn_output_norm.bias": "7eed5cd503bb6bb6ceb1bc8b07cc077903a4f14fb8b9d6cdf39644815ecf1374", + "blk.4.ffn_up.weight": "8d0c91d62e74d6431321116a37cf3339e630bd50ba164d3304fc4fe8dd831223", + "blk.4.ffn_up.bias": "d325f07f73c005a273c484c7be8e7abb4d6e8a5c4fd093f5869133b97629d017", + "blk.4.ffn_down.weight": "7ba7bd81143f40537b84f938e403e19f30e4928625eb371de052b9025beb4d21", + "blk.4.ffn_down.bias": "2853d9c2a75288214a4bf4907dc19d04d01926f4913d302b1aa7bdbfcce0f7a1", + "blk.4.layer_output_norm.weight": "a4ed1885fa77b90fed5300c355ef0aa0c876a8c747151d9d790939d464d57d4f", + "blk.4.layer_output_norm.bias": "62142a81e813a9e636333b2b805d6bc3b17c5e7cd4b15adce1ada6bc9a32563c", + "blk.5.attn_q.weight": "afc1dff080a72c3daad01384b1448d476aaf789871017c8ff8e144788887995d", + "blk.5.attn_q.bias": "748a820371c1d4f872c84545b36358d239c35bf6c99e2812c237d88c3292763b", + "blk.5.attn_k.weight": "59e30c1ed8acd2cbb01de5f62e7804015b9ecf98ba157d98cab016344639eda5", + "blk.5.attn_k.bias": "f839520078f9e589496e982e86d0126c7aa14196047339abffcf49a696229f77", + "blk.5.attn_v.weight": "3e21fb874e21b90308e1f46af034a3c32d3eba1628d62ae5f2246d6af5818923", + "blk.5.attn_v.bias": "5cd4852bf95c1444d10d756750f6bf49f842c0b39e9953c7f408bb67c325ac8c", + "blk.5.attn_output.weight": "636ce6a7752895f204b9d01ba0aedd9a294f908b42f372c22a16d9dd590d7471", + "blk.5.attn_output.bias": "82d924d4b0d2b94f2bbff91619216d6967a3541ce9b1531a6a60457a67b5d219", + "blk.5.attn_output_norm.weight": "5e7bd0a8d3396080f3360d7c4700bf094a06216431bd014c4479eef72ecf4271", + "blk.5.attn_output_norm.bias": "66c6de5edda5466d029c6753780be81ccd4218bf8bc00680000e0f06856ab712", + "blk.5.ffn_up.weight": "5bbf6e7ea380e216e33f8bee06d25f2265359d3876a300e92bc6e41d48e33430", + "blk.5.ffn_up.bias": "9d795388bb36fb33ad3a37fea3ccb4937838e02800a608fb47d363cd06b47370", + "blk.5.ffn_down.weight": "2fd628974e7f075479dd227b46fbd48ae8d3ca34d735b36f391ac06410730368", + "blk.5.ffn_down.bias": "cd213ba9eaa75fa541648097fbe9c96e58077e6c3ad6ad2fb1f21f8350f44291", + "blk.5.layer_output_norm.weight": "159a9df41d15b7022d136f86a2a2631c4635f9816e957472217077b522bcf52a", + "blk.5.layer_output_norm.bias": "24c1f27ffd1eb4e5be7e3a2909943e6f0980635d761fa1efdd0c19645da23766" +} diff --git a/convert/tokenizer.go b/convert/tokenizer.go index 0d42a6d8..653df6d2 100644 --- a/convert/tokenizer.go +++ b/convert/tokenizer.go @@ -1,7 +1,6 @@ package convert import ( - "cmp" "crypto/sha256" "encoding/hex" "encoding/json" @@ -11,6 +10,8 @@ import ( "log/slog" "os" "slices" + + "golang.org/x/exp/maps" ) const ( @@ -184,32 +185,32 @@ func parseVocabularyFromTokenizer(fsys fs.FS) (*Vocabulary, error) { return nil, err } - var tokens []token + tokens := make(map[int]token, len(t.Model.Vocab)) for k, v := range t.Model.Vocab { - tokens = append(tokens, token{ + tokens[v] = token{ ID: v, Content: k, - }) + } } - for _, t := range t.AddedTokens { - t.UserDefined = true - tokens = append(tokens, t) + for _, token := range t.AddedTokens { + token.UserDefined = true + tokens[token.ID] = token } - slices.SortFunc(tokens, func(i, j token) int { - return cmp.Compare(i.ID, j.ID) - }) + keys := maps.Keys(tokens) + slices.Sort(keys) v := Vocabulary{Model: "gpt2"} - for _, t := range tokens { - v.Tokens = append(v.Tokens, t.Content) - v.Scores = append(v.Scores, float32(t.ID)) + for _, k := range keys { + token := tokens[k] + v.Tokens = append(v.Tokens, token.Content) + v.Scores = append(v.Scores, float32(token.ID)) switch { - case t.Special: + case token.Special: v.Types = append(v.Types, tokenTypeControl) - case t.UserDefined: + case token.UserDefined: v.Types = append(v.Types, tokenTypeUserDefined) default: v.Types = append(v.Types, tokenTypeNormal) From beb49eef65acefc64a6ae0562ce58467e6974fde Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Fri, 7 Jun 2024 14:55:56 -0700 Subject: [PATCH 061/174] create bert models from cli --- cmd/cmd.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmd/cmd.go b/cmd/cmd.go index fd7246c8..a8a02605 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -223,6 +223,14 @@ func tempZipFiles(path string) (string, error) { } files = append(files, js...) + // bert models require a nested config.json + // TODO(mxyng): merge this with the glob above + js, err = glob(filepath.Join(path, "**/*.json"), "text/plain") + if err != nil { + return "", err + } + files = append(files, js...) + if tks, _ := glob(filepath.Join(path, "tokenizer.model"), "application/octet-stream"); len(tks) > 0 { // add tokenizer.model if it exists, tokenizer.json is automatically picked up by the previous glob // tokenizer.model might be a unresolved git lfs reference; error if it is @@ -252,6 +260,11 @@ func tempZipFiles(path string) (string, error) { return "", err } + zfi.Name, err = filepath.Rel(path, file) + if err != nil { + return "", err + } + zf, err := zipfile.CreateHeader(zfi) if err != nil { return "", err From 3546bbd08c52df73eb6523b06b13f1b2dfeaa5fb Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Fri, 28 Jun 2024 13:27:05 -0700 Subject: [PATCH 062/174] convert gemma2 --- convert/convert.go | 11 ++++++-- convert/convert_bert.go | 9 +++--- convert/convert_gemma.go | 14 ++++----- convert/convert_gemma2.go | 44 +++++++++++++++++++++++++++++ convert/convert_llama.go | 19 ++++++------- convert/convert_mixtral.go | 9 ++++-- convert/convert_phi3.go | 11 ++++---- convert/convert_test.go | 1 + convert/reader.go | 12 ++++---- convert/reader_safetensors.go | 5 ++-- convert/reader_torch.go | 5 ++-- convert/testdata/gemma-2-9b-it.json | 6 ++++ convert/tokenizer_spm.go | 32 ++++++++++++++++++++- 13 files changed, 132 insertions(+), 46 deletions(-) create mode 100644 convert/convert_gemma2.go create mode 100644 convert/testdata/gemma-2-9b-it.json diff --git a/convert/convert.go b/convert/convert.go index f51e9665..5a314cdd 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -7,6 +7,7 @@ import ( "io" "io/fs" "log/slog" + "strings" "github.com/ollama/ollama/llm" ) @@ -58,11 +59,13 @@ type Converter interface { KV(*Tokenizer) llm.KV // Tensors maps input tensors to LLM tensors. Model specific modifications can be done here. Tensors([]Tensor) []llm.Tensor + // Replacements returns a list of string pairs to replace in tensor names. + // See [strings.Replacer](https://pkg.go.dev/strings#Replacer) for details + Replacements() []string - // tensorName returns the LLM tensor name for a specific input name - tensorName(string) string // specialTokenTypes returns any special token types the model uses specialTokenTypes() []string + // writeFile writes the model to the provided io.WriteSeeker writeFile(io.WriteSeeker, llm.KV, []llm.Tensor) error } @@ -97,6 +100,8 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { conv = &mixtral{} case "GemmaForCausalLM": conv = &gemma{} + case "Gemma2ForCausalLM": + conv = &gemma2{} case "Phi3ForCausalLM": conv = &phi3{} case "BertModel": @@ -131,7 +136,7 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { slog.Debug("vocabulary", "size", len(t.Vocabulary.Tokens)) } - ts, err := parseTensors(fsys) + ts, err := parseTensors(fsys, strings.NewReplacer(conv.Replacements()...)) if err != nil { return err } diff --git a/convert/convert_bert.go b/convert/convert_bert.go index 62fad147..4547a705 100644 --- a/convert/convert_bert.go +++ b/convert/convert_bert.go @@ -144,9 +144,8 @@ func (p *bert) Tensors(ts []Tensor) []llm.Tensor { continue } - name := p.tensorName(t.Name()) out = append(out, llm.Tensor{ - Name: name, + Name: t.Name(), Kind: t.Kind(), Shape: t.Shape(), WriterTo: t, @@ -156,8 +155,8 @@ func (p *bert) Tensors(ts []Tensor) []llm.Tensor { return out } -func (bert) tensorName(n string) string { - return strings.NewReplacer( +func (bert) Replacements() []string { + return []string{ "encoder.layer", "blk", "encoder.layers", "blk", "embeddings.word_embeddings", "token_embd", @@ -172,5 +171,5 @@ func (bert) tensorName(n string) string { "intermediate.dense", "ffn_up", "output.dense", "ffn_down", "output.LayerNorm", "layer_output_norm", - ).Replace(n) + } } diff --git a/convert/convert_gemma.go b/convert/convert_gemma.go index 9213e157..333e4c83 100644 --- a/convert/convert_gemma.go +++ b/convert/convert_gemma.go @@ -44,15 +44,14 @@ func (p *gemma) KV(t *Tokenizer) llm.KV { } func (p *gemma) Tensors(ts []Tensor) []llm.Tensor { - var out []llm.Tensor + out := make([]llm.Tensor, 0, len(ts)) for _, t := range ts { - name := p.tensorName(t.Name()) - if strings.HasSuffix(name, "_norm.weight") { + if strings.HasSuffix(t.Name(), "_norm.weight") { t.SetRepacker(p.addOne) } out = append(out, llm.Tensor{ - Name: name, + Name: t.Name(), Kind: t.Kind(), Shape: t.Shape(), WriterTo: t, @@ -62,8 +61,8 @@ func (p *gemma) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *gemma) tensorName(n string) string { - return strings.NewReplacer( +func (p *gemma) Replacements() []string { + return []string{ "model.embed_tokens", "token_embd", "model.norm", "output_norm", "model.layers", "blk", @@ -76,8 +75,7 @@ func (p *gemma) tensorName(n string) string { "mlp.down_proj", "ffn_down", "mlp.up_proj", "ffn_up", "post_attention_layernorm", "ffn_norm", - "block_sparse_moe.gate", "ffn_inp", - ).Replace(n) + } } func (*gemma) addOne(_ string, data []float32, shape []uint64) ([]float32, error) { diff --git a/convert/convert_gemma2.go b/convert/convert_gemma2.go new file mode 100644 index 00000000..66be02d6 --- /dev/null +++ b/convert/convert_gemma2.go @@ -0,0 +1,44 @@ +package convert + +import ( + "github.com/ollama/ollama/llm" +) + +type gemma2 struct { + gemma + SlidingWindow uint32 `json:"sliding_window"` + AttentionLogitSoftcap float32 `json:"attn_logit_softcapping"` + FinalLogitSoftcap float32 `json:"final_logit_softcapping"` +} + +func (p *gemma2) KV(t *Tokenizer) llm.KV { + kv := p.Parameters.KV(t) + kv["general.architecture"] = "gemma2" + kv["general.name"] = "gemma2" + kv["gemma2.context_length"] = p.MaxPositionEmbeddings + kv["gemma2.embedding_length"] = p.HiddenSize + kv["gemma2.block_count"] = p.HiddenLayers + kv["gemma2.feed_forward_length"] = p.IntermediateSize + kv["gemma2.attention.head_count"] = p.NumAttentionHeads + kv["gemma2.attention.head_count_kv"] = p.NumKeyValueHeads + kv["gemma2.attention.layer_norm_rms_epsilon"] = p.RMSNormEPS + kv["gemma2.attention.key_length"] = p.HeadDim + kv["gemma2.attention.value_length"] = p.HeadDim + kv["gemma2.attention.sliding_window"] = p.SlidingWindow + kv["gemma2.attn_logit_softcapping"] = p.AttentionLogitSoftcap + kv["gemma2.final_logit_softcapping"] = p.FinalLogitSoftcap + kv["tokenizer.ggml.eot_token_id"] = uint32(107) + kv["tokenizer.ggml.middle_token_id"] = uint32(68) + kv["tokenizer.ggml.prefix_token_id"] = uint32(67) + kv["tokenizer.ggml.suffix_token_id"] = uint32(69) + return kv +} + +func (p *gemma2) Replacements() []string { + return append( + p.gemma.Replacements(), + "post_attention_layernorm", "post_attention_norm", + "pre_feedforward_layernorm", "ffn_norm", + "post_feedforward_layernorm", "post_ffw_norm", + ) +} diff --git a/convert/convert_llama.go b/convert/convert_llama.go index 178b13f3..498d1321 100644 --- a/convert/convert_llama.go +++ b/convert/convert_llama.go @@ -96,14 +96,13 @@ func (p *llama) KV(t *Tokenizer) llm.KV { func (p *llama) Tensors(ts []Tensor) []llm.Tensor { var out []llm.Tensor for _, t := range ts { - name := p.tensorName(t.Name()) - if strings.HasSuffix(name, "attn_q.weight") || - strings.HasSuffix(name, "attn_k.weight") { + if strings.HasSuffix(t.Name(), "attn_q.weight") || + strings.HasSuffix(t.Name(), "attn_k.weight") { t.SetRepacker(p.repack) } out = append(out, llm.Tensor{ - Name: name, + Name: t.Name(), Kind: t.Kind(), Shape: t.Shape(), WriterTo: t, @@ -113,8 +112,8 @@ func (p *llama) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *llama) tensorName(n string) string { - return strings.NewReplacer( +func (p *llama) Replacements() []string { + return []string{ "lm_head", "output", "model.embed_tokens", "token_embd", "model.norm", "output_norm", @@ -128,9 +127,7 @@ func (p *llama) tensorName(n string) string { "mlp.down_proj", "ffn_down", "mlp.up_proj", "ffn_up", "post_attention_layernorm", "ffn_norm", - // mixtral - "block_sparse_moe.gate", "ffn_gate_inp", - ).Replace(n) + } } func (p *llama) repack(name string, data []float32, shape []uint64) ([]float32, error) { @@ -140,9 +137,9 @@ func (p *llama) repack(name string, data []float32, shape []uint64) ([]float32, } var heads uint32 - if strings.HasSuffix(name, "q_proj.weight") { + if strings.HasSuffix(name, "attn_q.weight") { heads = p.NumAttentionHeads - } else if strings.HasSuffix(name, "k_proj.weight") { + } else if strings.HasSuffix(name, "attn_k.weight") { heads = cmp.Or(p.NumKeyValueHeads, p.NumAttentionHeads) } else { return nil, fmt.Errorf("unknown tensor for repack: %s", name) diff --git a/convert/convert_mixtral.go b/convert/convert_mixtral.go index 3263a27b..97a86b30 100644 --- a/convert/convert_mixtral.go +++ b/convert/convert_mixtral.go @@ -15,8 +15,6 @@ type mixtral struct { NumExpertsPerToken uint32 `json:"num_experts_per_tok"` } -var _ Converter = (*mixtral)(nil) - func (p *mixtral) KV(t *Tokenizer) llm.KV { kv := p.llama.KV(t) @@ -72,6 +70,13 @@ func (p *mixtral) Tensors(ts []Tensor) []llm.Tensor { return append(out, p.llama.Tensors(ts)...) } +func (p *mixtral) Replacements() []string { + return append( + p.llama.Replacements(), + "block_sparse_moe.gate", "ffn_gate_inp", + ) +} + type experts []Tensor func (e experts) WriteTo(w io.Writer) (int64, error) { diff --git a/convert/convert_phi3.go b/convert/convert_phi3.go index 0f645217..4ee59ff5 100644 --- a/convert/convert_phi3.go +++ b/convert/convert_phi3.go @@ -74,8 +74,7 @@ func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { out := make([]llm.Tensor, 0, len(ts)+2) for _, t := range ts { - name := p.tensorName(t.Name()) - if strings.HasPrefix(name, "blk.0.") { + if strings.HasPrefix(t.Name(), "blk.0.") { addRopeFactors.Do(func() { out = append(out, llm.Tensor{ Name: "rope_factors_long.weight", @@ -92,7 +91,7 @@ func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { } out = append(out, llm.Tensor{ - Name: name, + Name: t.Name(), Kind: t.Kind(), Shape: t.Shape(), WriterTo: t, @@ -102,8 +101,8 @@ func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *phi3) tensorName(n string) string { - return strings.NewReplacer( +func (p *phi3) Replacements() []string { + return []string{ "lm_head", "output", "model.embed_tokens", "token_embd", "model.norm", "output_norm", @@ -114,7 +113,7 @@ func (p *phi3) tensorName(n string) string { "mlp.down_proj", "ffn_down", "mlp.gate_up_proj", "ffn_up", "post_attention_layernorm", "ffn_norm", - ).Replace(n) + } } type ropeFactor []float32 diff --git a/convert/convert_test.go b/convert/convert_test.go index e3ab0098..e78afab7 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -68,6 +68,7 @@ func TestConvertFull(t *testing.T) { // microsoft/Phi-3-mini-128-instruct@d548c233192db00165d842bf8edff054bb3212f8 "Phi-3-mini-128k-instruct", "all-MiniLM-L6-v2", + "gemma-2-9b-it", } for i := range cases { diff --git a/convert/reader.go b/convert/reader.go index 294a7c40..5bba0406 100644 --- a/convert/reader.go +++ b/convert/reader.go @@ -35,9 +35,9 @@ const ( ) func (t tensorBase) Kind() uint32 { - if strings.HasSuffix(t.name, ".block_sparse_moe.gate.weight") { - return 0 - } else if t.name == "embeddings.token_type_embeddings.weight" { + if strings.HasSuffix(t.name, ".ffn_gate_inp.weight") || + t.name == "token_types.weight" { + // these tensors are always F32 return 0 } @@ -57,10 +57,10 @@ func (t *tensorBase) SetRepacker(fn repacker) { type repacker func(string, []float32, []uint64) ([]float32, error) -func parseTensors(fsys fs.FS) ([]Tensor, error) { +func parseTensors(fsys fs.FS, replacer *strings.Replacer) ([]Tensor, error) { patterns := []struct { Pattern string - Func func(fs.FS, ...string) ([]Tensor, error) + Func func(fs.FS, *strings.Replacer, ...string) ([]Tensor, error) }{ {"model-*-of-*.safetensors", parseSafetensors}, {"model.safetensors", parseSafetensors}, @@ -76,7 +76,7 @@ func parseTensors(fsys fs.FS) ([]Tensor, error) { } if len(matches) > 0 { - return pattern.Func(fsys, matches...) + return pattern.Func(fsys, replacer, matches...) } } diff --git a/convert/reader_safetensors.go b/convert/reader_safetensors.go index 42f902a5..32a362cd 100644 --- a/convert/reader_safetensors.go +++ b/convert/reader_safetensors.go @@ -8,6 +8,7 @@ import ( "io" "io/fs" "slices" + "strings" "github.com/d4l3k/go-bfloat16" "github.com/x448/float16" @@ -20,7 +21,7 @@ type safetensorMetadata struct { Offsets []int64 `json:"data_offsets"` } -func parseSafetensors(fsys fs.FS, ps ...string) ([]Tensor, error) { +func parseSafetensors(fsys fs.FS, replacer *strings.Replacer, ps ...string) ([]Tensor, error) { var ts []Tensor for _, p := range ps { f, err := fsys.Open(p) @@ -56,7 +57,7 @@ func parseSafetensors(fsys fs.FS, ps ...string) ([]Tensor, error) { offset: safetensorsPad(n, value.Offsets[0]), size: safetensorsPad(n, value.Offsets[1]) - safetensorsPad(n, value.Offsets[0]), tensorBase: &tensorBase{ - name: key, + name: replacer.Replace(key), shape: value.Shape, }, }) diff --git a/convert/reader_torch.go b/convert/reader_torch.go index 531996bf..1b3e1c9f 100644 --- a/convert/reader_torch.go +++ b/convert/reader_torch.go @@ -3,12 +3,13 @@ package convert import ( "io" "io/fs" + "strings" "github.com/nlpodyssey/gopickle/pytorch" "github.com/nlpodyssey/gopickle/types" ) -func parseTorch(fsys fs.FS, ps ...string) ([]Tensor, error) { +func parseTorch(fsys fs.FS, replacer *strings.Replacer, ps ...string) ([]Tensor, error) { var ts []Tensor for _, p := range ps { pt, err := pytorch.Load(p) @@ -27,7 +28,7 @@ func parseTorch(fsys fs.FS, ps ...string) ([]Tensor, error) { ts = append(ts, torch{ storage: t.(*pytorch.Tensor).Source, tensorBase: &tensorBase{ - name: k.(string), + name: replacer.Replace(k.(string)), shape: shape, }, }) diff --git a/convert/testdata/gemma-2-9b-it.json b/convert/testdata/gemma-2-9b-it.json new file mode 100644 index 00000000..90cdbee4 --- /dev/null +++ b/convert/testdata/gemma-2-9b-it.json @@ -0,0 +1,6 @@ +{ + "general.architecture": "gemma2", + "gemma2.attention.sliding_window": "4096", + "gemma2.attn_logit_softcapping": "50", + "gemma2.final_logit_softcapping": "30" +} diff --git a/convert/tokenizer_spm.go b/convert/tokenizer_spm.go index babf702c..5e506087 100644 --- a/convert/tokenizer_spm.go +++ b/convert/tokenizer_spm.go @@ -15,6 +15,11 @@ import ( ) func parseSentencePiece(fsys fs.FS) (*Vocabulary, error) { + ast, err := parseAdditionalSpecialTokens(fsys) + if err != nil { + return nil, err + } + bts, err := fs.ReadFile(fsys, "tokenizer.model") if err != nil { return nil, err @@ -37,7 +42,12 @@ func parseSentencePiece(fsys fs.FS) (*Vocabulary, error) { sentencepiece.ModelProto_SentencePiece_BYTE: v.Types = append(v.Types, int32(t)) default: - v.Types = append(v.Types, int32(sentencepiece.ModelProto_SentencePiece_NORMAL)) + tt := int32(sentencepiece.ModelProto_SentencePiece_NORMAL) + if slices.Contains(ast, piece.GetPiece()) { + tt = int32(sentencepiece.ModelProto_SentencePiece_CONTROL) + } + + v.Types = append(v.Types, tt) } } @@ -81,3 +91,23 @@ func parseSentencePiece(fsys fs.FS) (*Vocabulary, error) { return &v, nil } + +func parseAdditionalSpecialTokens(fsys fs.FS) ([]string, error) { + f, err := fsys.Open("special_tokens_map.json") + if errors.Is(err, os.ErrNotExist) { + return nil, nil + } else if err != nil { + return nil, err + } + defer f.Close() + + var m struct { + AdditionalSpecialTokens []string `json:"additional_special_tokens"` + } + + if err := json.NewDecoder(f).Decode(&m); err != nil { + return nil, err + } + + return m.AdditionalSpecialTokens, nil +} From 77903ab8b4fb8075faad7bde5bde2eee3173e407 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Mon, 29 Jul 2024 14:53:02 -0700 Subject: [PATCH 063/174] llama3.1 --- convert/convert_bert.go | 1 - convert/convert_gemma.go | 1 - convert/convert_gemma2.go | 1 - convert/convert_llama.go | 43 +++++++++++++++++-- convert/convert_phi3.go | 1 - convert/convert_test.go | 1 + .../testdata/Meta-Llama-3.1-8B-Instruct.json | 3 ++ llm/memory_test.go | 1 - server/sched_test.go | 1 - 9 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 convert/testdata/Meta-Llama-3.1-8B-Instruct.json diff --git a/convert/convert_bert.go b/convert/convert_bert.go index 4547a705..6e7d59fe 100644 --- a/convert/convert_bert.go +++ b/convert/convert_bert.go @@ -88,7 +88,6 @@ func (p *bert) parseMore(fsys fs.FS) error { func (p *bert) KV(t *Tokenizer) llm.KV { kv := p.Parameters.KV(t) kv["general.architecture"] = "bert" - kv["general.name"] = "bert" kv["bert.attention.causal"] = false kv["bert.pooling_type"] = p.PoolingType diff --git a/convert/convert_gemma.go b/convert/convert_gemma.go index 333e4c83..c4316808 100644 --- a/convert/convert_gemma.go +++ b/convert/convert_gemma.go @@ -26,7 +26,6 @@ var _ Converter = (*gemma)(nil) func (p *gemma) KV(t *Tokenizer) llm.KV { kv := p.Parameters.KV(t) kv["general.architecture"] = "gemma" - kv["general.name"] = "gemma" kv["gemma.context_length"] = p.MaxPositionEmbeddings kv["gemma.embedding_length"] = p.HiddenSize kv["gemma.block_count"] = p.HiddenLayers diff --git a/convert/convert_gemma2.go b/convert/convert_gemma2.go index 66be02d6..084f9c52 100644 --- a/convert/convert_gemma2.go +++ b/convert/convert_gemma2.go @@ -14,7 +14,6 @@ type gemma2 struct { func (p *gemma2) KV(t *Tokenizer) llm.KV { kv := p.Parameters.KV(t) kv["general.architecture"] = "gemma2" - kv["general.name"] = "gemma2" kv["gemma2.context_length"] = p.MaxPositionEmbeddings kv["gemma2.embedding_length"] = p.HiddenSize kv["gemma2.block_count"] = p.HiddenLayers diff --git a/convert/convert_llama.go b/convert/convert_llama.go index 498d1321..27f924fb 100644 --- a/convert/convert_llama.go +++ b/convert/convert_llama.go @@ -3,6 +3,7 @@ package convert import ( "cmp" "fmt" + "math" "strings" "github.com/pdevine/tensor" @@ -27,8 +28,14 @@ type llama struct { NumKeyValueHeads uint32 `json:"num_key_value_heads"` RopeTheta float32 `json:"rope_theta"` RopeScaling struct { - Type string `json:"type"` - Factor float32 `json:"factor"` + Type string `json:"type"` + RopeType string `json:"rope_type"` + Factor float32 `json:"factor"` + LowFrequencyFactor float32 `json:"low_freq_factor"` + HighFrequencyFactor float32 `json:"high_freq_factor"` + OriginalMaxPositionalEmbeddings uint32 `json:"original_max_positional_embeddings"` + + factors ropeFactor } `json:"rope_scaling"` RMSNormEPS float32 `json:"rms_norm_eps"` LayerNormEPS float32 `json:"layer_norm_eps"` @@ -42,7 +49,6 @@ var _ Converter = (*llama)(nil) func (p *llama) KV(t *Tokenizer) llm.KV { kv := p.Parameters.KV(t) kv["general.architecture"] = "llama" - kv["general.name"] = "llama" kv["llama.vocab_size"] = p.VocabSize kv["llama.block_count"] = cmp.Or(p.NLayers, p.NumHiddenLayers, p.NLayer) @@ -71,6 +77,27 @@ func (p *llama) KV(t *Tokenizer) llm.KV { if p.RopeScaling.Type == "linear" { kv["llama.rope.scaling.type"] = p.RopeScaling.Type kv["llama.rope.scaling.factor"] = p.RopeScaling.Factor + } else if p.RopeScaling.RopeType == "llama3" { + dim := p.HiddenSize / p.NumAttentionHeads + for i := uint32(0); i < dim; i += 2 { + factor := cmp.Or(p.RopeScaling.Factor, 8.0) + factorLow := cmp.Or(p.RopeScaling.LowFrequencyFactor, 1.0) + factorHigh := cmp.Or(p.RopeScaling.HighFrequencyFactor, 4.0) + + original := cmp.Or(p.RopeScaling.OriginalMaxPositionalEmbeddings, 8192) + lambdaLow := float32(original) / factorLow + lambdaHigh := float32(original) / factorHigh + + lambda := 2 * math.Pi * math.Pow(float64(p.RopeTheta), float64(i)/float64(dim)) + if lambda < float64(lambdaHigh) { + p.RopeScaling.factors = append(p.RopeScaling.factors, 1.0) + } else if lambda > float64(lambdaLow) { + p.RopeScaling.factors = append(p.RopeScaling.factors, factor) + } else { + smooth := (float32(original)/float32(lambda) - factorLow) / (factorHigh - factorLow) + p.RopeScaling.factors = append(p.RopeScaling.factors, 1.0/((1-smooth)/factor+smooth)) + } + } } if p.NumKeyValueHeads > 0 { @@ -95,6 +122,16 @@ func (p *llama) KV(t *Tokenizer) llm.KV { func (p *llama) Tensors(ts []Tensor) []llm.Tensor { var out []llm.Tensor + + if p.RopeScaling.factors != nil { + out = append(out, llm.Tensor{ + Name: "rope_freqs.weight", + Kind: 0, + Shape: []uint64{uint64(len(p.RopeScaling.factors))}, + WriterTo: p.RopeScaling.factors, + }) + } + for _, t := range ts { if strings.HasSuffix(t.Name(), "attn_q.weight") || strings.HasSuffix(t.Name(), "attn_k.weight") { diff --git a/convert/convert_phi3.go b/convert/convert_phi3.go index 4ee59ff5..64d3d012 100644 --- a/convert/convert_phi3.go +++ b/convert/convert_phi3.go @@ -40,7 +40,6 @@ var _ Converter = (*phi3)(nil) func (p *phi3) KV(t *Tokenizer) llm.KV { kv := p.Parameters.KV(t) kv["general.architecture"] = "phi3" - kv["general.name"] = "phi3" kv["phi3.context_length"] = p.MaxPositionEmbeddings kv["phi3.embedding_length"] = cmp.Or(p.HiddenSize, p.NEmbd) kv["phi3.feed_forward_length"] = p.IntermediateSize diff --git a/convert/convert_test.go b/convert/convert_test.go index e78afab7..64b7df3b 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -62,6 +62,7 @@ func TestMain(m *testing.M) { func TestConvertFull(t *testing.T) { cases := []string{ "Meta-Llama-3-8B-Instruct", + "Meta-Llama-3.1-8B-Instruct", "Mistral-7B-Instruct-v0.2", "Mixtral-8x7B-Instruct-v0.1", "gemma-2b-it", diff --git a/convert/testdata/Meta-Llama-3.1-8B-Instruct.json b/convert/testdata/Meta-Llama-3.1-8B-Instruct.json new file mode 100644 index 00000000..ad7cd20a --- /dev/null +++ b/convert/testdata/Meta-Llama-3.1-8B-Instruct.json @@ -0,0 +1,3 @@ +{ + "rope_freqs.weight": "80fd5efb2f729381785b293a091a268cfeceb0079167f6ece9b07070e662b222" +} diff --git a/llm/memory_test.go b/llm/memory_test.go index 6cf0119f..ffb14286 100644 --- a/llm/memory_test.go +++ b/llm/memory_test.go @@ -33,7 +33,6 @@ func TestEstimateGPULayers(t *testing.T) { assert.Len(t, tensors, inputLayerCount+1) err = WriteGGUF(f, KV{ "general.architecture": "llama", - "general.name": "name", "llama.context_length": uint32(32), "llama.embedding_length": uint32(4096), "llama.block_count": uint32(inputLayerCount), diff --git a/server/sched_test.go b/server/sched_test.go index 713b9259..fb049574 100644 --- a/server/sched_test.go +++ b/server/sched_test.go @@ -117,7 +117,6 @@ func newScenarioRequest(t *testing.T, ctx context.Context, modelName string, est require.NoError(t, llm.WriteGGUF(f, llm.KV{ "general.architecture": "llama", - "general.name": "name", "llama.context_length": uint32(32), "llama.embedding_length": uint32(4096), "llama.block_count": uint32(1), From 90ca84172c2a98ecfd76eb7e05cd3e33e1dde507 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Thu, 22 Aug 2024 14:51:42 -0700 Subject: [PATCH 064/174] Fix embeddings memory corruption (#6467) * Fix embeddings memory corruption The patch was leading to a buffer overrun corruption. Once removed though, parallism in server.cpp lead to hitting an assert due to slot/seq IDs being >= token count. To work around this, only use slot 0 for embeddings. * Fix embed integration test assumption The token eval count has changed with recent llama.cpp bumps (0.3.5+) --- integration/embed_test.go | 8 ++--- llm/ext_server/server.cpp | 8 ++++- llm/patches/08-pooling.diff | 60 ------------------------------------- server/sched.go | 5 ++++ 4 files changed, 16 insertions(+), 65 deletions(-) delete mode 100644 llm/patches/08-pooling.diff diff --git a/integration/embed_test.go b/integration/embed_test.go index 10333d5d..4a68af68 100644 --- a/integration/embed_test.go +++ b/integration/embed_test.go @@ -70,8 +70,8 @@ func TestAllMiniLMEmbed(t *testing.T) { t.Fatalf("expected 0.010071031, got %.8f", res.Embeddings[0][0]) } - if res.PromptEvalCount != 8 { - t.Fatalf("expected 8 prompt tokens, got %d", res.PromptEvalCount) + if res.PromptEvalCount != 6 { + t.Fatalf("expected 6 prompt tokens, got %d", res.PromptEvalCount) } } @@ -102,8 +102,8 @@ func TestAllMiniLMBatchEmbed(t *testing.T) { t.Fatalf("expected 0.010071031 and -0.009802706, got %.8f and %.8f", res.Embeddings[0][0], res.Embeddings[1][0]) } - if res.PromptEvalCount != 16 { - t.Fatalf("expected 16 prompt tokens, got %d", res.PromptEvalCount) + if res.PromptEvalCount != 12 { + t.Fatalf("expected 12 prompt tokens, got %d", res.PromptEvalCount) } } diff --git a/llm/ext_server/server.cpp b/llm/ext_server/server.cpp index 5717c17a..8e08b850 100644 --- a/llm/ext_server/server.cpp +++ b/llm/ext_server/server.cpp @@ -1429,7 +1429,13 @@ struct llama_server_context switch (task.type) { case TASK_TYPE_COMPLETION: { - server_slot *slot = prefix_slot(task.data["prompt"]); + server_slot *slot = nullptr; + if (task.embedding_mode) { + // Embedding seq_id (aka slot id) must always be <= token length, so always use slot 0 + slot = slots[0].available() ? &slots[0] : nullptr; + } else { + slot = prefix_slot(task.data["prompt"]); + } if (slot == nullptr) { // if no slot is available, we defer this task for processing later diff --git a/llm/patches/08-pooling.diff b/llm/patches/08-pooling.diff deleted file mode 100644 index 2e4fe11e..00000000 --- a/llm/patches/08-pooling.diff +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/src/llama.cpp b/src/llama.cpp -index 721b8f4e..cfe7ac40 100644 ---- a/src/llama.cpp -+++ b/src/llama.cpp -@@ -8420,14 +8420,14 @@ struct llm_build_context { - } - - struct ggml_tensor * build_inp_mean() { -- lctx.inp_mean = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_tokens, n_tokens); -+ lctx.inp_mean = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_tokens, cparams.n_seq_max); - cb(lctx.inp_mean, "inp_mean", -1); - ggml_set_input(lctx.inp_mean); - return lctx.inp_mean; - } - - struct ggml_tensor * build_inp_cls() { -- lctx.inp_cls = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_tokens); -+ lctx.inp_cls = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, cparams.n_seq_max); - cb(lctx.inp_cls, "inp_cls", -1); - ggml_set_input(lctx.inp_cls); - return lctx.inp_cls; -@@ -13847,19 +13847,16 @@ static void llama_set_inputs(llama_context & lctx, const llama_batch & batch) { - GGML_ASSERT(ggml_backend_buffer_is_host(lctx.inp_mean->buffer)); - - float * data = (float *) lctx.inp_mean->data; -- memset(lctx.inp_mean->data, 0, n_tokens * n_tokens * ggml_element_size(lctx.inp_mean)); -+ memset(lctx.inp_mean->data, 0, n_tokens * cparams.n_seq_max * ggml_element_size(lctx.inp_mean)); - - std::vector sum(n_tokens, 0); - for (int i = 0; i < n_tokens; ++i) { - const llama_seq_id seq_id = batch.seq_id[i][0]; -- -- GGML_ASSERT(seq_id < n_tokens && "seq_id cannot be larger than n_tokens with pooling_type == MEAN"); -- - sum[seq_id] += 1; - } - -- std::vector div(n_tokens, 0.0f); -- for (int i = 0; i < n_tokens; ++i) { -+ std::vector div(cparams.n_seq_max, 0.0f); -+ for (uint32_t i = 0; i < cparams.n_seq_max; ++i) { - const uint64_t s = sum[i]; - if (s > 0) { - div[i] = 1.0f/float(s); -@@ -13879,14 +13876,11 @@ static void llama_set_inputs(llama_context & lctx, const llama_batch & batch) { - GGML_ASSERT(ggml_backend_buffer_is_host(lctx.inp_cls->buffer)); - - uint32_t * data = (uint32_t *) lctx.inp_cls->data; -- memset(lctx.inp_cls->data, 0, n_tokens * ggml_element_size(lctx.inp_cls)); -+ memset(lctx.inp_cls->data, 0, cparams.n_seq_max * ggml_element_size(lctx.inp_cls)); - - for (int i = 0; i < n_tokens; ++i) { - const llama_seq_id seq_id = batch.seq_id[i][0]; - const llama_pos pos = batch.pos[i]; -- -- GGML_ASSERT(seq_id < n_tokens && "seq_id cannot be larger than n_tokens with pooling_type == CLS"); -- - if (pos == 0) { - data[seq_id] = i; - } diff --git a/server/sched.go b/server/sched.go index 9d8c4144..58071bf0 100644 --- a/server/sched.go +++ b/server/sched.go @@ -193,6 +193,11 @@ func (s *Scheduler) processPending(ctx context.Context) { break } + // Embedding models should always be loaded with parallel=1 + if pending.model.CheckCapabilities(CapabilityCompletion) != nil { + numParallel = 1 + } + // Evaluate if the model will fit in the available system memory, or if we should unload a model first if len(gpus) == 1 && gpus[0].Library == "cpu" { // simplifying assumption of defaultParallel when in CPU mode From 0b03b9c32f483be2d7a4e902d13a909b546ae6bf Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 23 Aug 2024 11:20:39 -0700 Subject: [PATCH 065/174] llm: Align cmake define for cuda no peer copy (#6455) Define changed recently and this slipped through the cracks with the old name. --- llm/generate/gen_linux.sh | 2 +- llm/generate/gen_windows.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llm/generate/gen_linux.sh b/llm/generate/gen_linux.sh index 6927dda8..1f702ca2 100755 --- a/llm/generate/gen_linux.sh +++ b/llm/generate/gen_linux.sh @@ -252,7 +252,7 @@ if [ -z "${OLLAMA_SKIP_ROCM_GENERATE}" -a -d "${ROCM_PATH}" ]; then ROCM_VARIANT=_v$(ls ${ROCM_PATH}/lib/librocblas.so.*.*.????? | cut -f5 -d. || true) fi init_vars - CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} -DGGML_HIPBLAS=on -DLLAMA_CUDA_NO_PEER_COPY=on -DCMAKE_C_COMPILER=$ROCM_PATH/llvm/bin/clang -DCMAKE_CXX_COMPILER=$ROCM_PATH/llvm/bin/clang++ -DAMDGPU_TARGETS=$(amdGPUs) -DGPU_TARGETS=$(amdGPUs)" + CMAKE_DEFS="${COMMON_CMAKE_DEFS} ${CMAKE_DEFS} -DGGML_HIPBLAS=on -DGGML_CUDA_NO_PEER_COPY=on -DCMAKE_C_COMPILER=$ROCM_PATH/llvm/bin/clang -DCMAKE_CXX_COMPILER=$ROCM_PATH/llvm/bin/clang++ -DAMDGPU_TARGETS=$(amdGPUs) -DGPU_TARGETS=$(amdGPUs)" # Users building from source can tune the exact flags we pass to cmake for configuring llama.cpp if [ -n "${OLLAMA_CUSTOM_ROCM_DEFS}" ]; then echo "OLLAMA_CUSTOM_ROCM_DEFS=\"${OLLAMA_CUSTOM_ROCM_DEFS}\"" diff --git a/llm/generate/gen_windows.ps1 b/llm/generate/gen_windows.ps1 index cbdfd09f..7179c1bc 100644 --- a/llm/generate/gen_windows.ps1 +++ b/llm/generate/gen_windows.ps1 @@ -355,7 +355,7 @@ function build_rocm() { "-DCMAKE_C_COMPILER=clang.exe", "-DCMAKE_CXX_COMPILER=clang++.exe", "-DGGML_HIPBLAS=on", - "-DLLAMA_CUDA_NO_PEER_COPY=on", + "-DGGML_CUDA_NO_PEER_COPY=on", "-DHIP_PLATFORM=amd", "-DGGML_AVX=on", "-DGGML_AVX2=off", From 7a1e1c1cafe4d3f3f935dc7192f9e66d4b2185b3 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 23 Aug 2024 11:21:12 -0700 Subject: [PATCH 066/174] gpu: Ensure driver version set before variant (#6480) During rebasing, the ordering was inverted causing the cuda version selection logic to break, with driver version being evaluated as zero incorrectly causing a downgrade to v11. --- gpu/gpu.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpu/gpu.go b/gpu/gpu.go index 72d237a6..10afb1e3 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -264,6 +264,8 @@ func GetGPUInfo() GpuInfoList { gpuInfo.computeMajor = int(memInfo.major) gpuInfo.computeMinor = int(memInfo.minor) gpuInfo.MinimumMemory = cudaMinimumMemory + gpuInfo.DriverMajor = driverMajor + gpuInfo.DriverMinor = driverMinor variant := cudaVariant(gpuInfo) if depPath != "" { gpuInfo.DependencyPath = depPath @@ -275,8 +277,6 @@ func GetGPUInfo() GpuInfoList { } } gpuInfo.Name = C.GoString(&memInfo.gpu_name[0]) - gpuInfo.DriverMajor = driverMajor - gpuInfo.DriverMinor = driverMinor gpuInfo.Variant = variant // query the management library as well so we can record any skew between the two From 0c819e167becd7f08312d2a1a1e2ac8e8ea5d4da Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Fri, 23 Aug 2024 11:29:56 -0700 Subject: [PATCH 067/174] convert safetensor adapters into GGUF (#6327) --- cmd/cmd.go | 6 + convert/convert.go | 111 +++++++++++-- convert/convert_bert.go | 18 +- convert/convert_gemma.go | 18 +- convert/convert_gemma2.go | 12 +- convert/convert_gemma2_adapter.go | 91 +++++++++++ convert/convert_llama.go | 16 +- convert/convert_llama_adapter.go | 169 +++++++++++++++++++ convert/convert_mixtral.go | 16 +- convert/convert_phi3.go | 14 +- convert/convert_test.go | 262 +++++++++++++++++++++++++++--- convert/reader.go | 2 + llm/ggml.go | 8 + server/images.go | 11 +- server/model.go | 38 ++++- server/model_test.go | 6 +- 16 files changed, 697 insertions(+), 101 deletions(-) create mode 100644 convert/convert_gemma2_adapter.go create mode 100644 convert/convert_llama_adapter.go diff --git a/cmd/cmd.go b/cmd/cmd.go index a8a02605..b75c0b5e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -204,6 +204,12 @@ func tempZipFiles(path string) (string, error) { // safetensors files might be unresolved git lfs references; skip if they are // covers model-x-of-y.safetensors, model.fp32-x-of-y.safetensors, model.safetensors files = append(files, st...) + } else if st, _ := glob(filepath.Join(path, "adapters.safetensors"), "application/octet-stream"); len(st) > 0 { + // covers adapters.safetensors + files = append(files, st...) + } else if st, _ := glob(filepath.Join(path, "adapter_model.safetensors"), "application/octet-stream"); len(st) > 0 { + // covers adapter_model.safetensors + files = append(files, st...) } else if pt, _ := glob(filepath.Join(path, "pytorch_model*.bin"), "application/zip"); len(pt) > 0 { // pytorch files might also be unresolved git lfs references; skip if they are // covers pytorch_model-x-of-y.bin, pytorch_model.fp32-x-of-y.bin, pytorch_model.bin diff --git a/convert/convert.go b/convert/convert.go index 5a314cdd..8c7b0943 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -12,12 +12,22 @@ import ( "github.com/ollama/ollama/llm" ) -type Parameters struct { +type ModelParameters struct { Architectures []string `json:"architectures"` VocabSize uint32 `json:"vocab_size"` } -func (Parameters) KV(t *Tokenizer) llm.KV { +type AdapterParameters struct { + Alpha uint32 `json:"lora_alpha"` + LoraLayers uint32 `json:"lora_layers"` + LoraParameters struct { + Rank uint32 `json:"rank"` + Alpha float32 `json:"alpha"` + Scale float32 `json:"scale"` + } `json:"lora_parameters"` +} + +func (ModelParameters) KV(t *Tokenizer) llm.KV { kv := llm.KV{ "general.file_type": uint32(1), "general.quantization_version": uint32(2), @@ -44,17 +54,40 @@ func (Parameters) KV(t *Tokenizer) llm.KV { return kv } -func (Parameters) specialTokenTypes() []string { +func (p AdapterParameters) KV() llm.KV { + var alpha float32 + if p.LoraParameters.Alpha == 0 { + alpha = float32(p.Alpha) + } else { + alpha = p.LoraParameters.Alpha + } + + kv := llm.KV{ + "adapter.lora.alpha": alpha, + "adapter.type": "lora", + "general.file_type": uint32(1), + "general.type": "adapter", + "general.version": "v0.2", + } + + return kv +} + +func (ModelParameters) specialTokenTypes() []string { return []string{ "bos", "eos", "unk", "sep", "pad", "cls", "mask", } } -func (Parameters) writeFile(ws io.WriteSeeker, kv llm.KV, ts []llm.Tensor) error { +func (ModelParameters) writeFile(ws io.WriteSeeker, kv llm.KV, ts []llm.Tensor) error { return llm.WriteGGUF(ws, kv, ts) } -type Converter interface { +func (AdapterParameters) writeFile(ws io.WriteSeeker, kv llm.KV, ts []llm.Tensor) error { + return llm.WriteGGUF(ws, kv, ts) +} + +type ModelConverter interface { // KV maps parameters to LLM key-values KV(*Tokenizer) llm.KV // Tensors maps input tensors to LLM tensors. Model specific modifications can be done here. @@ -73,17 +106,67 @@ type moreParser interface { parseMore(fs.FS) error } +type AdapterConverter interface { + // KV maps parameters to LLM key-values + KV(llm.KV) llm.KV + // Tensors maps input tensors to LLM tensors. Adapter specific modifications can be done here. + Tensors([]Tensor) []llm.Tensor + // Replacements returns a list of string pairs to replace in tensor names. + // See [strings.Replacer](https://pkg.go.dev/strings#Replacer) for details + Replacements() []string + + writeFile(io.WriteSeeker, llm.KV, []llm.Tensor) error +} + +func ConvertAdapter(fsys fs.FS, ws io.WriteSeeker, baseKV llm.KV) error { + bts, err := fs.ReadFile(fsys, "adapter_config.json") + if err != nil { + return err + } + + var p AdapterParameters + if err := json.Unmarshal(bts, &p); err != nil { + return err + } + + arch, ok := baseKV["general.architecture"] + if !ok { + return errors.New("architecture not set for the base model") + } + + var conv AdapterConverter + switch arch { + case "llama": + conv = &llamaAdapter{} + case "gemma2": + conv = &gemma2Adapter{} + default: + return errors.New("unsupported architecture") + } + + ts, err := parseTensors(fsys, strings.NewReplacer(conv.Replacements()...)) + if err != nil { + return err + } + + if err := json.Unmarshal(bts, conv); err != nil { + return err + } + + return conv.writeFile(ws, conv.KV(baseKV), conv.Tensors(ts)) +} + // Convert writes an Ollama compatible model to the provided io.WriteSeeker based on configurations // and files it finds in the input path. // Supported input model formats include safetensors. // Supported input tokenizers files include tokenizer.json (preferred) and tokenizer.model. -func Convert(fsys fs.FS, ws io.WriteSeeker) error { +func ConvertModel(fsys fs.FS, ws io.WriteSeeker) error { bts, err := fs.ReadFile(fsys, "config.json") if err != nil { return err } - var p Parameters + var p ModelParameters if err := json.Unmarshal(bts, &p); err != nil { return err } @@ -92,20 +175,20 @@ func Convert(fsys fs.FS, ws io.WriteSeeker) error { return errors.New("unknown architecture") } - var conv Converter + var conv ModelConverter switch p.Architectures[0] { case "LlamaForCausalLM", "MistralForCausalLM": - conv = &llama{} + conv = &llamaModel{} case "MixtralForCausalLM": - conv = &mixtral{} + conv = &mixtralModel{} case "GemmaForCausalLM": - conv = &gemma{} + conv = &gemmaModel{} case "Gemma2ForCausalLM": - conv = &gemma2{} + conv = &gemma2Model{} case "Phi3ForCausalLM": - conv = &phi3{} + conv = &phi3Model{} case "BertModel": - conv = &bert{} + conv = &bertModel{} default: return errors.New("unsupported architecture") } diff --git a/convert/convert_bert.go b/convert/convert_bert.go index 6e7d59fe..ea5facaa 100644 --- a/convert/convert_bert.go +++ b/convert/convert_bert.go @@ -11,8 +11,8 @@ import ( "github.com/ollama/ollama/llm" ) -type bert struct { - Parameters +type bertModel struct { + ModelParameters NLayers uint32 `json:"n_layers"` NumHiddenLayers uint32 `json:"num_hidden_layers"` NLayer uint32 `json:"n_layer"` @@ -33,11 +33,11 @@ type bert struct { } var ( - _ Converter = (*bert)(nil) - _ moreParser = (*bert)(nil) + _ ModelConverter = (*bertModel)(nil) + _ moreParser = (*bertModel)(nil) ) -func (p *bert) parseMore(fsys fs.FS) error { +func (p *bertModel) parseMore(fsys fs.FS) error { bts, err := fs.ReadFile(fsys, "modules.json") if err != nil { return err @@ -85,8 +85,8 @@ func (p *bert) parseMore(fsys fs.FS) error { return nil } -func (p *bert) KV(t *Tokenizer) llm.KV { - kv := p.Parameters.KV(t) +func (p *bertModel) KV(t *Tokenizer) llm.KV { + kv := p.ModelParameters.KV(t) kv["general.architecture"] = "bert" kv["bert.attention.causal"] = false kv["bert.pooling_type"] = p.PoolingType @@ -132,7 +132,7 @@ func (p *bert) KV(t *Tokenizer) llm.KV { return kv } -func (p *bert) Tensors(ts []Tensor) []llm.Tensor { +func (p *bertModel) Tensors(ts []Tensor) []llm.Tensor { var out []llm.Tensor for _, t := range ts { if slices.Contains([]string{ @@ -154,7 +154,7 @@ func (p *bert) Tensors(ts []Tensor) []llm.Tensor { return out } -func (bert) Replacements() []string { +func (bertModel) Replacements() []string { return []string{ "encoder.layer", "blk", "encoder.layers", "blk", diff --git a/convert/convert_gemma.go b/convert/convert_gemma.go index c4316808..b8865294 100644 --- a/convert/convert_gemma.go +++ b/convert/convert_gemma.go @@ -9,8 +9,8 @@ import ( "github.com/ollama/ollama/llm" ) -type gemma struct { - Parameters +type gemmaModel struct { + ModelParameters MaxPositionEmbeddings uint32 `json:"max_position_embeddings"` HiddenSize uint32 `json:"hidden_size"` HiddenLayers uint32 `json:"num_hidden_layers"` @@ -21,10 +21,10 @@ type gemma struct { HeadDim uint32 `json:"head_dim"` } -var _ Converter = (*gemma)(nil) +var _ ModelConverter = (*gemmaModel)(nil) -func (p *gemma) KV(t *Tokenizer) llm.KV { - kv := p.Parameters.KV(t) +func (p *gemmaModel) KV(t *Tokenizer) llm.KV { + kv := p.ModelParameters.KV(t) kv["general.architecture"] = "gemma" kv["gemma.context_length"] = p.MaxPositionEmbeddings kv["gemma.embedding_length"] = p.HiddenSize @@ -42,8 +42,8 @@ func (p *gemma) KV(t *Tokenizer) llm.KV { return kv } -func (p *gemma) Tensors(ts []Tensor) []llm.Tensor { - out := make([]llm.Tensor, 0, len(ts)) +func (p *gemmaModel) Tensors(ts []Tensor) []llm.Tensor { + var out []llm.Tensor for _, t := range ts { if strings.HasSuffix(t.Name(), "_norm.weight") { t.SetRepacker(p.addOne) @@ -60,7 +60,7 @@ func (p *gemma) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *gemma) Replacements() []string { +func (p *gemmaModel) Replacements() []string { return []string{ "model.embed_tokens", "token_embd", "model.norm", "output_norm", @@ -77,7 +77,7 @@ func (p *gemma) Replacements() []string { } } -func (*gemma) addOne(_ string, data []float32, shape []uint64) ([]float32, error) { +func (*gemmaModel) addOne(_ string, data []float32, shape []uint64) ([]float32, error) { n := tensor.New(tensor.WithShape(int(shape[0])), tensor.WithBacking(data)) ones := tensor.Ones(tensor.Float32, int(shape[0])) diff --git a/convert/convert_gemma2.go b/convert/convert_gemma2.go index 084f9c52..c4ee2d09 100644 --- a/convert/convert_gemma2.go +++ b/convert/convert_gemma2.go @@ -4,15 +4,15 @@ import ( "github.com/ollama/ollama/llm" ) -type gemma2 struct { - gemma +type gemma2Model struct { + gemmaModel SlidingWindow uint32 `json:"sliding_window"` AttentionLogitSoftcap float32 `json:"attn_logit_softcapping"` FinalLogitSoftcap float32 `json:"final_logit_softcapping"` } -func (p *gemma2) KV(t *Tokenizer) llm.KV { - kv := p.Parameters.KV(t) +func (p *gemma2Model) KV(t *Tokenizer) llm.KV { + kv := p.ModelParameters.KV(t) kv["general.architecture"] = "gemma2" kv["gemma2.context_length"] = p.MaxPositionEmbeddings kv["gemma2.embedding_length"] = p.HiddenSize @@ -33,9 +33,9 @@ func (p *gemma2) KV(t *Tokenizer) llm.KV { return kv } -func (p *gemma2) Replacements() []string { +func (p *gemma2Model) Replacements() []string { return append( - p.gemma.Replacements(), + p.gemmaModel.Replacements(), "post_attention_layernorm", "post_attention_norm", "pre_feedforward_layernorm", "ffn_norm", "post_feedforward_layernorm", "post_ffw_norm", diff --git a/convert/convert_gemma2_adapter.go b/convert/convert_gemma2_adapter.go new file mode 100644 index 00000000..a89a25f4 --- /dev/null +++ b/convert/convert_gemma2_adapter.go @@ -0,0 +1,91 @@ +package convert + +import ( + "strings" + + "github.com/pdevine/tensor" + "github.com/pdevine/tensor/native" + + "github.com/ollama/ollama/llm" +) + +type gemma2Adapter struct { + AdapterParameters +} + +var _ AdapterConverter = (*gemma2Adapter)(nil) + +func (p *gemma2Adapter) KV(baseKV llm.KV) llm.KV { + kv := p.AdapterParameters.KV() + kv["general.architecture"] = "gemma2" + return kv +} + +func (p *gemma2Adapter) Tensors(ts []Tensor) []llm.Tensor { + var out []llm.Tensor + for _, t := range ts { + shape := t.Shape() + if (strings.HasSuffix(t.Name(), "weight.lora_a") && shape[0] > shape[1]) || + (strings.HasSuffix(t.Name(), "weight.lora_b") && shape[0] < shape[1]) { + shape[0], shape[1] = shape[1], shape[0] + t.SetRepacker(p.repack) + } + + out = append(out, llm.Tensor{ + Name: t.Name(), + Kind: t.Kind(), + Shape: t.Shape(), + WriterTo: t, + }) + } + + return out +} + +func (p *gemma2Adapter) Replacements() []string { + return []string{ + "base_model.model.", "", + "model.layers", "blk", + "self_attn.q_proj", "attn_q", + "self_attn.k_proj", "attn_k", + "self_attn.v_proj", "attn_v", + "self_attn.o_proj", "attn_output", + "mlp.gate_proj", "ffn_gate", + "mlp.down_proj", "ffn_down", + "mlp.up_proj", "ffn_up", + "lora_A.weight", "weight.lora_a", + "lora_B.weight", "weight.lora_b", + "lora_a", "weight.lora_a", + "lora_b", "weight.lora_b", + } +} + +func (p *gemma2Adapter) repack(name string, data []float32, shape []uint64) ([]float32, error) { + dims := []int{int(shape[1]), int(shape[0])} + + n := tensor.New(tensor.WithShape(dims...), tensor.WithBacking(data)) + + if err := n.T(1, 0); err != nil { + return nil, err + } + + if err := n.Reshape(dims...); err != nil { + return nil, err + } + + if err := n.Transpose(); err != nil { + return nil, err + } + + ts, err := native.SelectF32(n, 1) + if err != nil { + return nil, err + } + + var f32s []float32 + for _, t := range ts { + f32s = append(f32s, t...) + } + + return f32s, nil +} diff --git a/convert/convert_llama.go b/convert/convert_llama.go index 27f924fb..5dedb829 100644 --- a/convert/convert_llama.go +++ b/convert/convert_llama.go @@ -12,8 +12,8 @@ import ( "github.com/ollama/ollama/llm" ) -type llama struct { - Parameters +type llamaModel struct { + ModelParameters NLayers uint32 `json:"n_layers"` NumHiddenLayers uint32 `json:"num_hidden_layers"` NLayer uint32 `json:"n_layer"` @@ -44,10 +44,10 @@ type llama struct { HeadDim uint32 `json:"head_dim"` } -var _ Converter = (*llama)(nil) +var _ ModelConverter = (*llamaModel)(nil) -func (p *llama) KV(t *Tokenizer) llm.KV { - kv := p.Parameters.KV(t) +func (p *llamaModel) KV(t *Tokenizer) llm.KV { + kv := p.ModelParameters.KV(t) kv["general.architecture"] = "llama" kv["llama.vocab_size"] = p.VocabSize @@ -120,7 +120,7 @@ func (p *llama) KV(t *Tokenizer) llm.KV { return kv } -func (p *llama) Tensors(ts []Tensor) []llm.Tensor { +func (p *llamaModel) Tensors(ts []Tensor) []llm.Tensor { var out []llm.Tensor if p.RopeScaling.factors != nil { @@ -149,7 +149,7 @@ func (p *llama) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *llama) Replacements() []string { +func (p *llamaModel) Replacements() []string { return []string{ "lm_head", "output", "model.embed_tokens", "token_embd", @@ -167,7 +167,7 @@ func (p *llama) Replacements() []string { } } -func (p *llama) repack(name string, data []float32, shape []uint64) ([]float32, error) { +func (p *llamaModel) repack(name string, data []float32, shape []uint64) ([]float32, error) { var dims []int for _, dim := range shape { dims = append(dims, int(dim)) diff --git a/convert/convert_llama_adapter.go b/convert/convert_llama_adapter.go new file mode 100644 index 00000000..08ddee10 --- /dev/null +++ b/convert/convert_llama_adapter.go @@ -0,0 +1,169 @@ +package convert + +import ( + "cmp" + "strings" + + "github.com/pdevine/tensor" + "github.com/pdevine/tensor/native" + + "github.com/ollama/ollama/llm" +) + +type llamaAdapter struct { + AdapterParameters + NumAttentionHeads uint32 `json:"num_attention_heads"` + NumKeyValueHeads uint32 `json:"num_key_value_heads"` +} + +var _ AdapterConverter = (*llamaAdapter)(nil) + +func (p *llamaAdapter) KV(baseKV llm.KV) llm.KV { + kv := p.AdapterParameters.KV() + kv["general.architecture"] = "llama" + kv["llama.attention.head_count"] = baseKV["llama.attention.head_count"] + kv["llama.attention.head_count_kv"] = baseKV["llama.attention.head_count_kv"] + + p.NumAttentionHeads = baseKV["llama.attention.head_count"].(uint32) + + return kv +} + +func (p *llamaAdapter) Tensors(ts []Tensor) []llm.Tensor { + var out []llm.Tensor + for _, t := range ts { + shape := t.Shape() + if (strings.HasSuffix(t.Name(), "weight.lora_a") && shape[0] > shape[1]) || + (strings.HasSuffix(t.Name(), "weight.lora_b") && shape[0] < shape[1]) { + shape[0], shape[1] = shape[1], shape[0] + t.SetRepacker(p.repackAndTranspose) + } else { + t.SetRepacker(p.repack) + } + + out = append(out, llm.Tensor{ + Name: t.Name(), + Kind: t.Kind(), + Shape: shape, + WriterTo: t, + }) + } + + return out +} + +func (p *llamaAdapter) Replacements() []string { + return []string{ + "base_model.model.", "", + "model.layers", "blk", + "self_attn.q_proj", "attn_q", + "self_attn.k_proj", "attn_k", + "self_attn.v_proj", "attn_v", + "self_attn.o_proj", "attn_output", + "mlp.gate_proj", "ffn_gate", + "mlp.down_proj", "ffn_down", + "mlp.up_proj", "ffn_up", + "lora_A.weight", "weight.lora_a", + "lora_B.weight", "weight.lora_b", + "lora_a", "weight.lora_a", + "lora_b", "weight.lora_b", + } +} + +func (p *llamaAdapter) repack(name string, data []float32, shape []uint64) ([]float32, error) { + dims := []int{int(shape[1]), int(shape[0])} + + var heads uint32 + if strings.HasSuffix(name, "attn_q.weight.lora_a") { + heads = p.NumAttentionHeads + } else if strings.HasSuffix(name, "attn_k.weight.lora_a") { + heads = cmp.Or(p.NumKeyValueHeads, p.NumAttentionHeads) + } else { + return data, nil + } + + n := tensor.New(tensor.WithShape(dims...), tensor.WithBacking(data)) + + if err := n.Reshape(append([]int{int(heads), 2, dims[0] / int(heads) / 2}, dims[1:]...)...); err != nil { + return nil, err + } + + if err := n.T(0, 2, 1, 3); err != nil { + return nil, err + } + + if err := n.Reshape(dims...); err != nil { + return nil, err + } + + if err := n.Transpose(); err != nil { + return nil, err + } + + ts, err := native.SelectF32(n, 1) + if err != nil { + return nil, err + } + + var f32s []float32 + for _, t := range ts { + f32s = append(f32s, t...) + } + + return f32s, nil +} + +func (p *llamaAdapter) repackAndTranspose(name string, data []float32, shape []uint64) ([]float32, error) { + dims := []int{int(shape[1]), int(shape[0])} + + n := tensor.New(tensor.WithShape(dims...), tensor.WithBacking(data)) + + var heads uint32 + if strings.HasSuffix(name, "attn_q.weight.lora_a") { + heads = p.NumAttentionHeads + } else if strings.HasSuffix(name, "attn_k.weight.lora_a") { + heads = cmp.Or(p.NumKeyValueHeads, p.NumAttentionHeads) + } + + if heads > 0 { + if err := n.Reshape(append([]int{int(heads), 2, dims[0] / int(heads) / 2}, dims[1:]...)...); err != nil { + return nil, err + } + + if err := n.T(0, 2, 1, 3); err != nil { + return nil, err + } + + if err := n.Reshape(dims...); err != nil { + return nil, err + } + + if err := n.Transpose(); err != nil { + return nil, err + } + } + + if err := n.T(1, 0); err != nil { + return nil, err + } + + if err := n.Reshape(dims...); err != nil { + return nil, err + } + + if err := n.Transpose(); err != nil { + return nil, err + } + + ts, err := native.SelectF32(n, 1) + if err != nil { + return nil, err + } + + var f32s []float32 + for _, t := range ts { + f32s = append(f32s, t...) + } + + return f32s, nil +} diff --git a/convert/convert_mixtral.go b/convert/convert_mixtral.go index 97a86b30..43b7c8b1 100644 --- a/convert/convert_mixtral.go +++ b/convert/convert_mixtral.go @@ -9,14 +9,14 @@ import ( "github.com/ollama/ollama/llm" ) -type mixtral struct { - llama +type mixtralModel struct { + llamaModel NumLocalExperts uint32 `json:"num_local_experts"` NumExpertsPerToken uint32 `json:"num_experts_per_tok"` } -func (p *mixtral) KV(t *Tokenizer) llm.KV { - kv := p.llama.KV(t) +func (p *mixtralModel) KV(t *Tokenizer) llm.KV { + kv := p.llamaModel.KV(t) if p.NumLocalExperts > 0 { kv["llama.expert_count"] = p.NumLocalExperts @@ -29,7 +29,7 @@ func (p *mixtral) KV(t *Tokenizer) llm.KV { return kv } -func (p *mixtral) Tensors(ts []Tensor) []llm.Tensor { +func (p *mixtralModel) Tensors(ts []Tensor) []llm.Tensor { oldnew := []string{ "model.layers", "blk", "w1", "ffn_gate_exps", @@ -67,12 +67,12 @@ func (p *mixtral) Tensors(ts []Tensor) []llm.Tensor { }) } - return append(out, p.llama.Tensors(ts)...) + return append(out, p.llamaModel.Tensors(ts)...) } -func (p *mixtral) Replacements() []string { +func (p *mixtralModel) Replacements() []string { return append( - p.llama.Replacements(), + p.llamaModel.Replacements(), "block_sparse_moe.gate", "ffn_gate_inp", ) } diff --git a/convert/convert_phi3.go b/convert/convert_phi3.go index 64d3d012..3de0d404 100644 --- a/convert/convert_phi3.go +++ b/convert/convert_phi3.go @@ -11,8 +11,8 @@ import ( "github.com/ollama/ollama/llm" ) -type phi3 struct { - Parameters +type phi3Model struct { + ModelParameters NumHiddenLayers uint32 `json:"num_hidden_layers"` NLayers uint32 `json:"n_layers"` HiddenSize uint32 `json:"hidden_size"` @@ -35,10 +35,10 @@ type phi3 struct { SlidingWindow uint32 `json:"sliding_window"` } -var _ Converter = (*phi3)(nil) +var _ ModelConverter = (*phi3Model)(nil) -func (p *phi3) KV(t *Tokenizer) llm.KV { - kv := p.Parameters.KV(t) +func (p *phi3Model) KV(t *Tokenizer) llm.KV { + kv := p.ModelParameters.KV(t) kv["general.architecture"] = "phi3" kv["phi3.context_length"] = p.MaxPositionEmbeddings kv["phi3.embedding_length"] = cmp.Or(p.HiddenSize, p.NEmbd) @@ -68,7 +68,7 @@ func (p *phi3) KV(t *Tokenizer) llm.KV { return kv } -func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { +func (p *phi3Model) Tensors(ts []Tensor) []llm.Tensor { var addRopeFactors sync.Once out := make([]llm.Tensor, 0, len(ts)+2) @@ -100,7 +100,7 @@ func (p *phi3) Tensors(ts []Tensor) []llm.Tensor { return out } -func (p *phi3) Replacements() []string { +func (p *phi3Model) Replacements() []string { return []string{ "lm_head", "output", "model.embed_tokens", "token_embd", diff --git a/convert/convert_test.go b/convert/convert_test.go index 64b7df3b..56b34f22 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -1,7 +1,9 @@ package convert import ( + "bytes" "crypto/sha256" + "encoding/binary" "encoding/hex" "encoding/json" "flag" @@ -29,7 +31,7 @@ func convertFull(t *testing.T, fsys fs.FS) (*os.File, llm.KV, llm.Tensors) { } defer f.Close() - if err := Convert(fsys, f); err != nil { + if err := ConvertModel(fsys, f); err != nil { t.Fatal(err) } @@ -51,6 +53,34 @@ func convertFull(t *testing.T, fsys fs.FS) (*os.File, llm.KV, llm.Tensors) { return r, m.KV(), m.Tensors() } +func generateResultsJSON(t *testing.T, f *os.File, kv llm.KV, tensors llm.Tensors) map[string]string { + actual := make(map[string]string) + for k, v := range kv { + if s, ok := v.(json.Marshaler); !ok { + actual[k] = fmt.Sprintf("%v", v) + } else { + bts, err := json.Marshal(s) + if err != nil { + t.Fatal(err) + } + + actual[k] = fmt.Sprintf("%x", sha256.Sum256(bts)) + } + } + + for _, tensor := range tensors.Items { + sha256sum := sha256.New() + sr := io.NewSectionReader(f, int64(tensors.Offset+tensor.Offset), int64(tensor.Size())) + if _, err := io.Copy(sha256sum, sr); err != nil { + t.Fatal(err) + } + + actual[tensor.Name] = hex.EncodeToString(sha256sum.Sum(nil)) + } + + return actual +} + func TestMain(m *testing.M) { var level slog.Level flag.TextVar(&level, "level", slog.LevelInfo, "log level") @@ -85,29 +115,7 @@ func TestConvertFull(t *testing.T) { } f, kv, tensors := convertFull(t, os.DirFS(p)) - actual := make(map[string]string) - for k, v := range kv { - if s, ok := v.(json.Marshaler); !ok { - actual[k] = fmt.Sprintf("%v", v) - } else { - bts, err := json.Marshal(s) - if err != nil { - t.Fatal(err) - } - - actual[k] = fmt.Sprintf("%x", sha256.Sum256(bts)) - } - } - - for _, tensor := range tensors.Items { - sha256sum := sha256.New() - sr := io.NewSectionReader(f, int64(tensors.Offset+tensor.Offset), int64(tensor.Size())) - if _, err := io.Copy(sha256sum, sr); err != nil { - t.Fatal(err) - } - - actual[tensor.Name] = hex.EncodeToString(sha256sum.Sum(nil)) - } + actual := generateResultsJSON(t, f, kv, tensors) expectFile, err := os.Open(filepath.Join("testdata", fmt.Sprintf("%s.json", tt))) if err != nil { @@ -131,3 +139,209 @@ func TestConvertFull(t *testing.T) { }) } } + +func TestConvertAdapter(t *testing.T) { + type AdapterCase struct { + Name string + BaseKV map[string]any + Expected map[string]string + } + + cases := []AdapterCase{ + { + Name: "discollama", + BaseKV: map[string]any{ + "general.architecture": "llama", + "llama.attention.head_count": uint32(32), + "llama.attention.head_count_kv": uint32(8), + }, + Expected: map[string]string{ + "general.architecture": "llama", + "general.file_type": "1", + "general.parameter_count": "106496", + "general.type": "adapter", + "general.version": "v0.2", + "adapter.lora.alpha": "16", + "adapter.type": "lora", + "llama.attention.head_count": "32", + "llama.attention.head_count_kv": "8", + "blk.31.attn_q.weight.lora_a": "0eb3318b02cd313429bcc7621b539fdbb10240fea190c56c9e5f93fcd37a4e50", + "blk.31.attn_q.weight.lora_b": "0eb3318b02cd313429bcc7621b539fdbb10240fea190c56c9e5f93fcd37a4e50", + "blk.31.attn_v.weight.lora_a": "0eb3318b02cd313429bcc7621b539fdbb10240fea190c56c9e5f93fcd37a4e50", + "blk.31.attn_v.weight.lora_b": "071dcafe89df065d6e1c935ecb8fdf6479b3c202eb912e7da938597673ff5857", + }, + }, + } + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + t.Parallel() + + f, err := os.CreateTemp(t.TempDir(), "f16") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + tempDir := t.TempDir() + generateLoraTestData(t, tempDir) + + if err = ConvertAdapter(os.DirFS(tempDir), f, c.BaseKV); err != nil { + t.Fatal(err) + } + + r, err := os.Open(f.Name()) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + m, _, err := llm.DecodeGGML(r, math.MaxInt) + if err != nil { + t.Fatal(err) + } + + if _, err := r.Seek(0, io.SeekStart); err != nil { + t.Fatal(err) + } + + actual := generateResultsJSON(t, r, m.KV(), m.Tensors()) + + keys := maps.Keys(c.Expected) + slices.Sort(keys) + for _, k := range keys { + if v, ok := actual[k]; !ok { + t.Errorf("missing %s", k) + } else if v != c.Expected[k] { + t.Errorf("unexpected %s: want %s, got %s", k, c.Expected[k], v) + } + } + }) + } +} + +func generateLoraTestData(t *testing.T, tempDir string) { + type tensorData struct { + Offsets []int `json:"data_offsets"` + Type string `json:"dtype"` + Shape []int `json:"shape"` + } + offset := 4096 * 8 * 4 + + td := map[string]*tensorData{"__metadata__": nil} + td["model.layers.31.self_attn.q_proj.lora_a"] = &tensorData{ + Offsets: []int{0, offset}, + Type: "F32", + Shape: []int{4096, 8}, + } + td["model.layers.31.self_attn.q_proj.lora_b"] = &tensorData{ + Offsets: []int{offset, offset * 2}, + Type: "F32", + Shape: []int{8, 4096}, + } + td["model.layers.31.self_attn.v_proj.lora_a"] = &tensorData{ + Offsets: []int{offset * 2, offset * 3}, + Type: "F32", + Shape: []int{4096, 8}, + } + td["model.layers.31.self_attn.v_proj.lora_b"] = &tensorData{ + Offsets: []int{offset * 3, offset*3 + 8*1024*4}, + Type: "F32", + Shape: []int{8, 1024}, + } + + data, err := json.Marshal(td) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + l := int64(len(data)) + err = binary.Write(&buf, binary.LittleEndian, l) + if err != nil { + t.Fatal(err) + } + + _, err = buf.Write(data) + if err != nil { + t.Fatal(err) + } + + // write some data for the tensors + + ones := make([]float32, 4096*8) + for i := range ones { + ones[i] = float32(1) + } + + for range 3 { + err = binary.Write(&buf, binary.LittleEndian, ones) + if err != nil { + t.Fatal(err) + } + } + + ones = make([]float32, 1024*8) + for i := range ones { + ones[i] = float32(1) + } + + err = binary.Write(&buf, binary.LittleEndian, ones) + if err != nil { + t.Fatal(err) + } + + fdata, err := os.Create(filepath.Join(tempDir, "adapters.safetensors")) + if err != nil { + t.Fatal(err) + } + defer fdata.Close() + + _, err = fdata.Write(buf.Bytes()) + if err != nil { + t.Fatal(err) + } + + configData := ` +{ + "adapter_path": "adapters-test", + "batch_size": 8, + "config": "config-tiny.json", + "data": "../discollama-completion", + "grad_checkpoint": null, + "iters": 1000, + "learning_rate": 1e-05, + "lora_layers": 1, + "lora_parameters": { + "rank": 8, + "alpha": 16, + "dropout": 0.0, + "scale": 2.0 + }, + "lr_schedule": null, + "max_seq_length": 2048, + "model": "/Users/pdevine/git/Meta-Llama-3-8B-Instruct", + "resume_adapter_file": null, + "save_every": 100, + "seed": 0, + "steps_per_eval": 200, + "steps_per_report": 10, + "test": false, + "test_batches": 500, + "train": true, + "use_dora": false, + "val_batches": 25 +} +` + f, err := os.Create(filepath.Join(tempDir, "adapter_config.json")) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + _, err = f.WriteString(configData) + if err != nil { + t.Fatal(err) + } +} diff --git a/convert/reader.go b/convert/reader.go index 5bba0406..c1218e66 100644 --- a/convert/reader.go +++ b/convert/reader.go @@ -64,6 +64,8 @@ func parseTensors(fsys fs.FS, replacer *strings.Replacer) ([]Tensor, error) { }{ {"model-*-of-*.safetensors", parseSafetensors}, {"model.safetensors", parseSafetensors}, + {"adapters.safetensors", parseSafetensors}, + {"adapter_model.safetensors", parseSafetensors}, {"pytorch_model-*-of-*.bin", parseTorch}, {"pytorch_model.bin", parseTorch}, {"consolidated.*.pth", parseTorch}, diff --git a/llm/ggml.go b/llm/ggml.go index 4c68adf9..ab436095 100644 --- a/llm/ggml.go +++ b/llm/ggml.go @@ -43,6 +43,14 @@ func (kv KV) Architecture() string { return "unknown" } +func (kv KV) Kind() string { + if s, ok := kv["general.type"].(string); ok { + return s + } + + return "unknown" +} + func (kv KV) ParameterCount() uint64 { return kv.u64("general.parameter_count") } diff --git a/server/images.go b/server/images.go index 8b3a67cf..b5bf7ad6 100644 --- a/server/images.go +++ b/server/images.go @@ -369,13 +369,14 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio parameters := make(map[string]any) var layers []Layer + var baseLayers []*layerGGML for _, c := range modelfile.Commands { mediatype := fmt.Sprintf("application/vnd.ollama.image.%s", c.Name) + command := c.Name - switch c.Name { + switch command { case "model", "adapter": - var baseLayers []*layerGGML - if name := model.ParseName(c.Args); name.IsValid() { + if name := model.ParseName(c.Args); name.IsValid() && command == "model" { baseLayers, err = parseFromModel(ctx, name, fn) if err != nil { return err @@ -409,14 +410,14 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio } defer blob.Close() - baseLayers, err = parseFromFile(ctx, blob, digest, fn) + baseLayers, err = parseFromFile(ctx, command, baseLayers, blob, digest, fn) if err != nil { return err } } else if file, err := os.Open(realpath(modelFileDir, c.Args)); err == nil { defer file.Close() - baseLayers, err = parseFromFile(ctx, file, "", fn) + baseLayers, err = parseFromFile(ctx, command, baseLayers, file, "", fn) if err != nil { return err } diff --git a/server/model.go b/server/model.go index b17bf0e3..55fb2d8d 100644 --- a/server/model.go +++ b/server/model.go @@ -81,7 +81,7 @@ func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressRe return layers, nil } -func parseFromZipFile(_ context.Context, f *os.File, digest string, fn func(api.ProgressResponse)) (layers []*layerGGML, err error) { +func parseFromZipFile(_ context.Context, command string, baseLayers []*layerGGML, f *os.File, digest string, fn func(api.ProgressResponse)) (layers []*layerGGML, err error) { fi, err := f.Stat() if err != nil { return nil, err @@ -108,16 +108,38 @@ func parseFromZipFile(_ context.Context, f *os.File, digest string, fn func(api. defer t.Close() defer os.Remove(t.Name()) - fn(api.ProgressResponse{Status: "converting model"}) - if err := convert.Convert(convert.NewZipReader(r, p, 32<<20), t); err != nil { - return nil, err + var layerType string + + switch command { + case "adapter": + var baseModel *llm.GGML + for _, l := range baseLayers { + if l.GGML != nil { + baseModel = l.GGML + break + } + } + + if baseModel == nil { + return nil, fmt.Errorf("no base model specified for the adapter") + } + + if err := convert.ConvertAdapter(convert.NewZipReader(r, p, 32<<20), t, baseModel.KV()); err != nil { + return nil, err + } + layerType = "application/vnd.ollama.image.adapter" + case "model": + if err := convert.ConvertModel(convert.NewZipReader(r, p, 32<<20), t); err != nil { + return nil, err + } + layerType = "application/vnd.ollama.image.model" } if _, err := t.Seek(0, io.SeekStart); err != nil { return nil, err } - layer, err := NewLayer(t, "application/vnd.ollama.image.model") + layer, err := NewLayer(t, layerType) if err != nil { return nil, err } @@ -139,7 +161,7 @@ func parseFromZipFile(_ context.Context, f *os.File, digest string, fn func(api. return detectChatTemplate(layers) } -func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(api.ProgressResponse)) (layers []*layerGGML, err error) { +func parseFromFile(ctx context.Context, command string, baseLayers []*layerGGML, file *os.File, digest string, fn func(api.ProgressResponse)) (layers []*layerGGML, err error) { sr := io.NewSectionReader(file, 0, 512) contentType, err := detectContentType(sr) if err != nil { @@ -150,7 +172,7 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap case "gguf", "ggla": // noop case "application/zip": - return parseFromZipFile(ctx, file, digest, fn) + return parseFromZipFile(ctx, command, baseLayers, file, digest, fn) default: return nil, fmt.Errorf("unsupported content type: %s", contentType) } @@ -170,7 +192,7 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap } mediatype := "application/vnd.ollama.image.model" - if ggml.Name() == "ggla" { + if ggml.Name() == "ggla" || ggml.KV().Kind() == "adapter" { mediatype = "application/vnd.ollama.image.adapter" } else if ggml.KV().Architecture() == "clip" { mediatype = "application/vnd.ollama.image.projector" diff --git a/server/model_test.go b/server/model_test.go index 63fc408d..7753c549 100644 --- a/server/model_test.go +++ b/server/model_test.go @@ -153,7 +153,7 @@ func TestParseFromFileFromLayer(t *testing.T) { t.Fatalf("failed to seek to start: %v", err) } - layers, err := parseFromFile(context.Background(), file, "", func(api.ProgressResponse) {}) + layers, err := parseFromFile(context.Background(), "model", []*layerGGML{}, file, "", func(api.ProgressResponse) {}) if err != nil { t.Fatalf("failed to parse from file: %v", err) } @@ -166,7 +166,7 @@ func TestParseFromFileFromLayer(t *testing.T) { t.Fatalf("failed to seek to start: %v", err) } - layers2, err := parseFromFile(context.Background(), file, layers[0].Digest, func(api.ProgressResponse) {}) + layers2, err := parseFromFile(context.Background(), "model", []*layerGGML{}, file, layers[0].Digest, func(api.ProgressResponse) {}) if err != nil { t.Fatalf("failed to parse from file: %v", err) } @@ -206,7 +206,7 @@ func TestParseLayerFromCopy(t *testing.T) { t.Fatalf("failed to seek to start: %v", err) } - layers, err := parseFromFile(context.Background(), file2, "", func(api.ProgressResponse) {}) + layers, err := parseFromFile(context.Background(), "model", []*layerGGML{}, file2, "", func(api.ProgressResponse) {}) if err != nil { t.Fatalf("failed to parse from file: %v", err) } From 386af6c1a0e9b1f4788b000ddf98955b4ce6183b Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Fri, 23 Aug 2024 13:16:30 -0700 Subject: [PATCH 068/174] passthrough OLLAMA_HOST path to client --- envconfig/config.go | 10 +++------ envconfig/config_test.go | 47 ++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/envconfig/config.go b/envconfig/config.go index 7e45a4f5..c13167b5 100644 --- a/envconfig/config.go +++ b/envconfig/config.go @@ -30,9 +30,7 @@ func Host() *url.URL { defaultPort = "443" } - // trim trailing slashes - hostport = strings.TrimRight(hostport, "/") - + hostport, path, _ := strings.Cut(hostport, "/") host, port, err := net.SplitHostPort(hostport) if err != nil { host, port = "127.0.0.1", defaultPort @@ -45,15 +43,13 @@ func Host() *url.URL { if n, err := strconv.ParseInt(port, 10, 32); err != nil || n > 65535 || n < 0 { slog.Warn("invalid port, using default", "port", port, "default", defaultPort) - return &url.URL{ - Scheme: scheme, - Host: net.JoinHostPort(host, defaultPort), - } + port = defaultPort } return &url.URL{ Scheme: scheme, Host: net.JoinHostPort(host, port), + Path: path, } } diff --git a/envconfig/config_test.go b/envconfig/config_test.go index 92a500f1..d52a98a5 100644 --- a/envconfig/config_test.go +++ b/envconfig/config_test.go @@ -13,34 +13,35 @@ func TestHost(t *testing.T) { value string expect string }{ - "empty": {"", "127.0.0.1:11434"}, - "only address": {"1.2.3.4", "1.2.3.4:11434"}, - "only port": {":1234", ":1234"}, - "address and port": {"1.2.3.4:1234", "1.2.3.4:1234"}, - "hostname": {"example.com", "example.com:11434"}, - "hostname and port": {"example.com:1234", "example.com:1234"}, - "zero port": {":0", ":0"}, - "too large port": {":66000", ":11434"}, - "too small port": {":-1", ":11434"}, - "ipv6 localhost": {"[::1]", "[::1]:11434"}, - "ipv6 world open": {"[::]", "[::]:11434"}, - "ipv6 no brackets": {"::1", "[::1]:11434"}, - "ipv6 + port": {"[::1]:1337", "[::1]:1337"}, - "extra space": {" 1.2.3.4 ", "1.2.3.4:11434"}, - "extra quotes": {"\"1.2.3.4\"", "1.2.3.4:11434"}, - "extra space+quotes": {" \" 1.2.3.4 \" ", "1.2.3.4:11434"}, - "extra single quotes": {"'1.2.3.4'", "1.2.3.4:11434"}, - "http": {"http://1.2.3.4", "1.2.3.4:80"}, - "http port": {"http://1.2.3.4:4321", "1.2.3.4:4321"}, - "https": {"https://1.2.3.4", "1.2.3.4:443"}, - "https port": {"https://1.2.3.4:4321", "1.2.3.4:4321"}, + "empty": {"", "http://127.0.0.1:11434"}, + "only address": {"1.2.3.4", "http://1.2.3.4:11434"}, + "only port": {":1234", "http://:1234"}, + "address and port": {"1.2.3.4:1234", "http://1.2.3.4:1234"}, + "hostname": {"example.com", "http://example.com:11434"}, + "hostname and port": {"example.com:1234", "http://example.com:1234"}, + "zero port": {":0", "http://:0"}, + "too large port": {":66000", "http://:11434"}, + "too small port": {":-1", "http://:11434"}, + "ipv6 localhost": {"[::1]", "http://[::1]:11434"}, + "ipv6 world open": {"[::]", "http://[::]:11434"}, + "ipv6 no brackets": {"::1", "http://[::1]:11434"}, + "ipv6 + port": {"[::1]:1337", "http://[::1]:1337"}, + "extra space": {" 1.2.3.4 ", "http://1.2.3.4:11434"}, + "extra quotes": {"\"1.2.3.4\"", "http://1.2.3.4:11434"}, + "extra space+quotes": {" \" 1.2.3.4 \" ", "http://1.2.3.4:11434"}, + "extra single quotes": {"'1.2.3.4'", "http://1.2.3.4:11434"}, + "http": {"http://1.2.3.4", "http://1.2.3.4:80"}, + "http port": {"http://1.2.3.4:4321", "http://1.2.3.4:4321"}, + "https": {"https://1.2.3.4", "https://1.2.3.4:443"}, + "https port": {"https://1.2.3.4:4321", "https://1.2.3.4:4321"}, + "proxy path": {"https://example.com/ollama", "https://example.com:443/ollama"}, } for name, tt := range cases { t.Run(name, func(t *testing.T) { t.Setenv("OLLAMA_HOST", tt.value) - if host := Host(); host.Host != tt.expect { - t.Errorf("%s: expected %s, got %s", name, tt.expect, host.Host) + if host := Host(); host.String() != tt.expect { + t.Errorf("%s: expected %s, got %s", name, tt.expect, host.String()) } }) } From bb362caf88487a08cb29c8263d383a7d9e448803 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 2 Jul 2024 15:02:07 -0700 Subject: [PATCH 069/174] update faq --- docs/faq.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 324116d1..25b68248 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -111,7 +111,10 @@ On Windows, Ollama inherits your user and system environment variables. ## How do I use Ollama behind a proxy? -Ollama is compatible with proxy servers if `HTTP_PROXY` or `HTTPS_PROXY` are configured. When using either variables, ensure it is set where `ollama serve` can access the values. When using `HTTPS_PROXY`, ensure the proxy certificate is installed as a system certificate. Refer to the section above for how to use environment variables on your platform. +Ollama pulls models from the Internet and may require a proxy server to access the models. Use `HTTPS_PROXY` to redirect outbound requests through the proxy. Ensure the proxy certificate is installed as a system certificate. Refer to the section above for how to use environment variables on your platform. + +> [!NOTE] +> Avoid setting `HTTP_PROXY`. Ollama does not use HTTP for model pulls, only HTTPS. Setting `HTTP_PROXY` may interrupt client connections to the server. ### How do I use Ollama behind a proxy in Docker? @@ -276,4 +279,4 @@ Note: Windows with Radeon GPUs currently default to 1 model maximum due to limit ## How does Ollama load models on multiple GPUs? -Installing multiple GPUs of the same brand can be a great way to increase your available VRAM to load larger models. When you load a new model, Ollama evaluates the required VRAM for the model against what is currently available. If the model will entirely fit on any single GPU, Ollama will load the model on that GPU. This typically provides the best performance as it reduces the amount of data transfering across the PCI bus during inference. If the model does not fit entirely on one GPU, then it will be spread across all the available GPUs. \ No newline at end of file +Installing multiple GPUs of the same brand can be a great way to increase your available VRAM to load larger models. When you load a new model, Ollama evaluates the required VRAM for the model against what is currently available. If the model will entirely fit on any single GPU, Ollama will load the model on that GPU. This typically provides the best performance as it reduces the amount of data transfering across the PCI bus during inference. If the model does not fit entirely on one GPU, then it will be spread across all the available GPUs. From 69be940bf6d2816f61c79facfa336183bc882720 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 23 Aug 2024 15:11:56 -0700 Subject: [PATCH 070/174] gpu: Group GPU Library sets by variant (#6483) The recent cuda variant changes uncovered a bug in ByLibrary which failed to group by common variant for GPU types. --- gpu/gpu_test.go | 25 +++++++++++++++++++++++++ gpu/types.go | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/gpu/gpu_test.go b/gpu/gpu_test.go index 46d3201e..13a3f544 100644 --- a/gpu/gpu_test.go +++ b/gpu/gpu_test.go @@ -32,4 +32,29 @@ func TestCPUMemInfo(t *testing.T) { } } +func TestByLibrary(t *testing.T) { + type testCase struct { + input []GpuInfo + expect int + } + + testCases := map[string]*testCase{ + "empty": {input: []GpuInfo{}, expect: 0}, + "cpu": {input: []GpuInfo{{Library: "cpu"}}, expect: 1}, + "cpu + GPU": {input: []GpuInfo{{Library: "cpu"}, {Library: "cuda"}}, expect: 2}, + "cpu + 2 GPU no variant": {input: []GpuInfo{{Library: "cpu"}, {Library: "cuda"}, {Library: "cuda"}}, expect: 2}, + "cpu + 2 GPU same variant": {input: []GpuInfo{{Library: "cpu"}, {Library: "cuda", Variant: "v11"}, {Library: "cuda", Variant: "v11"}}, expect: 2}, + "cpu + 2 GPU diff variant": {input: []GpuInfo{{Library: "cpu"}, {Library: "cuda", Variant: "v11"}, {Library: "cuda", Variant: "v12"}}, expect: 3}, + } + + for k, v := range testCases { + t.Run(k, func(t *testing.T) { + resp := (GpuInfoList)(v.input).ByLibrary() + if len(resp) != v.expect { + t.Fatalf("expected length %d, got %d => %+v", v.expect, len(resp), resp) + } + }) + } +} + // TODO - add some logic to figure out card type through other means and actually verify we got back what we expected diff --git a/gpu/types.go b/gpu/types.go index 4cbbeb84..a30e5fb3 100644 --- a/gpu/types.go +++ b/gpu/types.go @@ -94,7 +94,7 @@ func (l GpuInfoList) ByLibrary() []GpuInfoList { } } if !found { - libs = append(libs, info.Library) + libs = append(libs, requested) resp = append(resp, []GpuInfo{info}) } } From 0f92b19bec97198b035a7801eda14e3d48149033 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Sat, 24 Aug 2024 17:24:50 -0700 Subject: [PATCH 071/174] Only enable numa on CPUs (#6484) The numa flag may be having a performance impact on multi-socket systems with GPU loads --- llm/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llm/server.go b/llm/server.go index 9347a458..4e5dac28 100644 --- a/llm/server.go +++ b/llm/server.go @@ -258,7 +258,7 @@ func NewLlamaServer(gpus gpu.GpuInfoList, model string, ggml *GGML, adapters, pr params = append(params, "--mlock") } - if gpu.IsNUMA() { + if gpu.IsNUMA() && gpus[0].Library == "cpu" { numaMode := "distribute" if runtime.GOOS == "linux" { if _, err := exec.LookPath("numactl"); err == nil { From 47fa0839b970f7ad8dbb35344440bb51d5a7ba56 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 26 Aug 2024 19:36:11 -0700 Subject: [PATCH 072/174] server: clean up route names for consistency (#6524) --- server/routes.go | 36 ++++++++++++++++---------------- server/routes_create_test.go | 38 +++++++++++++++++----------------- server/routes_delete_test.go | 10 ++++----- server/routes_generate_test.go | 14 ++++++------- server/routes_list_test.go | 4 ++-- server/routes_test.go | 12 +++++------ 6 files changed, 57 insertions(+), 57 deletions(-) diff --git a/server/routes.go b/server/routes.go index 6c470c17..5e9f51e1 100644 --- a/server/routes.go +++ b/server/routes.go @@ -463,7 +463,7 @@ func (s *Server) EmbeddingsHandler(c *gin.Context) { c.JSON(http.StatusOK, resp) } -func (s *Server) PullModelHandler(c *gin.Context) { +func (s *Server) PullHandler(c *gin.Context) { var req api.PullRequest err := c.ShouldBindJSON(&req) switch { @@ -513,7 +513,7 @@ func (s *Server) PullModelHandler(c *gin.Context) { streamResponse(c, ch) } -func (s *Server) PushModelHandler(c *gin.Context) { +func (s *Server) PushHandler(c *gin.Context) { var req api.PushRequest err := c.ShouldBindJSON(&req) switch { @@ -577,7 +577,7 @@ func checkNameExists(name model.Name) error { return nil } -func (s *Server) CreateModelHandler(c *gin.Context) { +func (s *Server) CreateHandler(c *gin.Context) { var r api.CreateRequest if err := c.ShouldBindJSON(&r); errors.Is(err, io.EOF) { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing request body"}) @@ -647,7 +647,7 @@ func (s *Server) CreateModelHandler(c *gin.Context) { streamResponse(c, ch) } -func (s *Server) DeleteModelHandler(c *gin.Context) { +func (s *Server) DeleteHandler(c *gin.Context) { var r api.DeleteRequest if err := c.ShouldBindJSON(&r); errors.Is(err, io.EOF) { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing request body"}) @@ -680,7 +680,7 @@ func (s *Server) DeleteModelHandler(c *gin.Context) { } } -func (s *Server) ShowModelHandler(c *gin.Context) { +func (s *Server) ShowHandler(c *gin.Context) { var req api.ShowRequest err := c.ShouldBindJSON(&req) switch { @@ -829,7 +829,7 @@ func getKVData(digest string, verbose bool) (llm.KV, error) { return kv, nil } -func (s *Server) ListModelsHandler(c *gin.Context) { +func (s *Server) ListHandler(c *gin.Context) { ms, err := Manifests() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -879,7 +879,7 @@ func (s *Server) ListModelsHandler(c *gin.Context) { c.JSON(http.StatusOK, api.ListResponse{Models: models}) } -func (s *Server) CopyModelHandler(c *gin.Context) { +func (s *Server) CopyHandler(c *gin.Context) { var r api.CopyRequest if err := c.ShouldBindJSON(&r); errors.Is(err, io.EOF) { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing request body"}) @@ -1081,33 +1081,33 @@ func (s *Server) GenerateRoutes() http.Handler { allowedHostsMiddleware(s.addr), ) - r.POST("/api/pull", s.PullModelHandler) + r.POST("/api/pull", s.PullHandler) r.POST("/api/generate", s.GenerateHandler) r.POST("/api/chat", s.ChatHandler) r.POST("/api/embed", s.EmbedHandler) r.POST("/api/embeddings", s.EmbeddingsHandler) - r.POST("/api/create", s.CreateModelHandler) - r.POST("/api/push", s.PushModelHandler) - r.POST("/api/copy", s.CopyModelHandler) - r.DELETE("/api/delete", s.DeleteModelHandler) - r.POST("/api/show", s.ShowModelHandler) + r.POST("/api/create", s.CreateHandler) + r.POST("/api/push", s.PushHandler) + r.POST("/api/copy", s.CopyHandler) + r.DELETE("/api/delete", s.DeleteHandler) + r.POST("/api/show", s.ShowHandler) r.POST("/api/blobs/:digest", s.CreateBlobHandler) r.HEAD("/api/blobs/:digest", s.HeadBlobHandler) - r.GET("/api/ps", s.ProcessHandler) + r.GET("/api/ps", s.PsHandler) // Compatibility endpoints r.POST("/v1/chat/completions", openai.ChatMiddleware(), s.ChatHandler) r.POST("/v1/completions", openai.CompletionsMiddleware(), s.GenerateHandler) r.POST("/v1/embeddings", openai.EmbeddingsMiddleware(), s.EmbedHandler) - r.GET("/v1/models", openai.ListMiddleware(), s.ListModelsHandler) - r.GET("/v1/models/:model", openai.RetrieveMiddleware(), s.ShowModelHandler) + r.GET("/v1/models", openai.ListMiddleware(), s.ListHandler) + r.GET("/v1/models/:model", openai.RetrieveMiddleware(), s.ShowHandler) for _, method := range []string{http.MethodGet, http.MethodHead} { r.Handle(method, "/", func(c *gin.Context) { c.String(http.StatusOK, "Ollama is running") }) - r.Handle(method, "/api/tags", s.ListModelsHandler) + r.Handle(method, "/api/tags", s.ListHandler) r.Handle(method, "/api/version", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"version": version.Version}) }) @@ -1269,7 +1269,7 @@ func streamResponse(c *gin.Context, ch chan any) { }) } -func (s *Server) ProcessHandler(c *gin.Context) { +func (s *Server) PsHandler(c *gin.Context) { models := []api.ProcessModelResponse{} for _, v := range s.sched.loaded { diff --git a/server/routes_create_test.go b/server/routes_create_test.go index 4de07b25..d436f26c 100644 --- a/server/routes_create_test.go +++ b/server/routes_create_test.go @@ -93,7 +93,7 @@ func TestCreateFromBin(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)), Stream: &stream, @@ -120,7 +120,7 @@ func TestCreateFromModel(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)), Stream: &stream, @@ -134,7 +134,7 @@ func TestCreateFromModel(t *testing.T) { filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"), }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test2", Modelfile: "FROM test", Stream: &stream, @@ -162,7 +162,7 @@ func TestCreateRemovesLayers(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .Prompt }}", createBinFile(t, nil, nil)), Stream: &stream, @@ -182,7 +182,7 @@ func TestCreateRemovesLayers(t *testing.T) { filepath.Join(p, "blobs", "sha256-bc80b03733773e0728011b2f4adf34c458b400e1aad48cb28d61170f3a2ad2d6"), }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .System }} {{ .Prompt }}", createBinFile(t, nil, nil)), Stream: &stream, @@ -210,7 +210,7 @@ func TestCreateUnsetsSystem(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nSYSTEM Say hi!", createBinFile(t, nil, nil)), Stream: &stream, @@ -230,7 +230,7 @@ func TestCreateUnsetsSystem(t *testing.T) { filepath.Join(p, "blobs", "sha256-f29e82a8284dbdf5910b1555580ff60b04238b8da9d5e51159ada67a4d0d5851"), }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nSYSTEM \"\"", createBinFile(t, nil, nil)), Stream: &stream, @@ -267,7 +267,7 @@ func TestCreateMergeParameters(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nPARAMETER temperature 1\nPARAMETER top_k 10\nPARAMETER stop USER:\nPARAMETER stop ASSISTANT:", createBinFile(t, nil, nil)), Stream: &stream, @@ -288,7 +288,7 @@ func TestCreateMergeParameters(t *testing.T) { }) // in order to merge parameters, the second model must be created FROM the first - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test2", Modelfile: "FROM test\nPARAMETER temperature 0.6\nPARAMETER top_p 0.7", Stream: &stream, @@ -326,7 +326,7 @@ func TestCreateMergeParameters(t *testing.T) { } // slices are replaced - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test2", Modelfile: "FROM test\nPARAMETER temperature 0.6\nPARAMETER top_p 0.7\nPARAMETER stop <|endoftext|>", Stream: &stream, @@ -371,7 +371,7 @@ func TestCreateReplacesMessages(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nMESSAGE assistant \"What is my purpose?\"\nMESSAGE user \"You run tests.\"\nMESSAGE assistant \"Oh, my god.\"", createBinFile(t, nil, nil)), Stream: &stream, @@ -391,7 +391,7 @@ func TestCreateReplacesMessages(t *testing.T) { filepath.Join(p, "blobs", "sha256-e0e27d47045063ccb167ae852c51d49a98eab33fabaee4633fdddf97213e40b5"), }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test2", Modelfile: "FROM test\nMESSAGE assistant \"You're a test, Harry.\"\nMESSAGE user \"I-I'm a what?\"\nMESSAGE assistant \"A test. And a thumping good one at that, I'd wager.\"", Stream: &stream, @@ -448,7 +448,7 @@ func TestCreateTemplateSystem(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .Prompt }}\nSYSTEM Say hello!\nTEMPLATE {{ .System }} {{ .Prompt }}\nSYSTEM Say bye!", createBinFile(t, nil, nil)), Stream: &stream, @@ -488,7 +488,7 @@ func TestCreateTemplateSystem(t *testing.T) { } t.Run("incomplete template", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .Prompt", createBinFile(t, nil, nil)), Stream: &stream, @@ -500,7 +500,7 @@ func TestCreateTemplateSystem(t *testing.T) { }) t.Run("template with unclosed if", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ if .Prompt }}", createBinFile(t, nil, nil)), Stream: &stream, @@ -512,7 +512,7 @@ func TestCreateTemplateSystem(t *testing.T) { }) t.Run("template with undefined function", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ Prompt }}", createBinFile(t, nil, nil)), Stream: &stream, @@ -531,7 +531,7 @@ func TestCreateLicenses(t *testing.T) { t.Setenv("OLLAMA_MODELS", p) var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s\nLICENSE MIT\nLICENSE Apache-2.0", createBinFile(t, nil, nil)), Stream: &stream, @@ -579,7 +579,7 @@ func TestCreateDetectTemplate(t *testing.T) { var s Server t.Run("matched", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, llm.KV{ "tokenizer.chat_template": "{{ bos_token }}{% for message in messages %}{{'<|' + message['role'] + '|>' + '\n' + message['content'] + '<|end|>\n' }}{% endfor %}{% if add_generation_prompt %}{{ '<|assistant|>\n' }}{% else %}{{ eos_token }}{% endif %}", @@ -600,7 +600,7 @@ func TestCreateDetectTemplate(t *testing.T) { }) t.Run("unmatched", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)), Stream: &stream, diff --git a/server/routes_delete_test.go b/server/routes_delete_test.go index 82fac9f5..5a337e79 100644 --- a/server/routes_delete_test.go +++ b/server/routes_delete_test.go @@ -22,7 +22,7 @@ func TestDelete(t *testing.T) { var s Server - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)), }) @@ -31,7 +31,7 @@ func TestDelete(t *testing.T) { t.Fatalf("expected status code 200, actual %d", w.Code) } - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Name: "test2", Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .System }} {{ .Prompt }}", createBinFile(t, nil, nil)), }) @@ -52,7 +52,7 @@ func TestDelete(t *testing.T) { filepath.Join(p, "blobs", "sha256-fe7ac77b725cda2ccad03f88a880ecdfd7a33192d6cae08fce2c0ee1455991ed"), }) - w = createRequest(t, s.DeleteModelHandler, api.DeleteRequest{Name: "test"}) + w = createRequest(t, s.DeleteHandler, api.DeleteRequest{Name: "test"}) if w.Code != http.StatusOK { t.Fatalf("expected status code 200, actual %d", w.Code) @@ -68,7 +68,7 @@ func TestDelete(t *testing.T) { filepath.Join(p, "blobs", "sha256-fe7ac77b725cda2ccad03f88a880ecdfd7a33192d6cae08fce2c0ee1455991ed"), }) - w = createRequest(t, s.DeleteModelHandler, api.DeleteRequest{Name: "test2"}) + w = createRequest(t, s.DeleteHandler, api.DeleteRequest{Name: "test2"}) if w.Code != http.StatusOK { t.Fatalf("expected status code 200, actual %d", w.Code) @@ -102,7 +102,7 @@ func TestDeleteDuplicateLayers(t *testing.T) { t.Fatal(err) } - w := createRequest(t, s.DeleteModelHandler, api.DeleteRequest{Name: "test"}) + w := createRequest(t, s.DeleteHandler, api.DeleteRequest{Name: "test"}) if w.Code != http.StatusOK { t.Errorf("expected status code 200, actual %d", w.Code) } diff --git a/server/routes_generate_test.go b/server/routes_generate_test.go index 5c0caff1..480b9672 100644 --- a/server/routes_generate_test.go +++ b/server/routes_generate_test.go @@ -84,7 +84,7 @@ func TestGenerateChat(t *testing.T) { go s.sched.Run(context.TODO()) - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "test", Modelfile: fmt.Sprintf(`FROM %s TEMPLATE """ @@ -144,7 +144,7 @@ func TestGenerateChat(t *testing.T) { }) t.Run("missing capabilities chat", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "bert", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, llm.KV{ "general.architecture": "bert", @@ -270,7 +270,7 @@ func TestGenerateChat(t *testing.T) { checkChatResponse(t, w.Body, "test", "Hi!") }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "test-system", Modelfile: "FROM test\nSYSTEM You are a helpful assistant.", }) @@ -382,7 +382,7 @@ func TestGenerate(t *testing.T) { go s.sched.Run(context.TODO()) - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "test", Modelfile: fmt.Sprintf(`FROM %s TEMPLATE """ @@ -442,7 +442,7 @@ func TestGenerate(t *testing.T) { }) t.Run("missing capabilities generate", func(t *testing.T) { - w := createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w := createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "bert", Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, llm.KV{ "general.architecture": "bert", @@ -583,7 +583,7 @@ func TestGenerate(t *testing.T) { checkGenerateResponse(t, w.Body, "test", "Hi!") }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "test-system", Modelfile: "FROM test\nSYSTEM You are a helpful assistant.", }) @@ -652,7 +652,7 @@ func TestGenerate(t *testing.T) { checkGenerateResponse(t, w.Body, "test-system", "Abra kadabra!") }) - w = createRequest(t, s.CreateModelHandler, api.CreateRequest{ + w = createRequest(t, s.CreateHandler, api.CreateRequest{ Model: "test-suffix", Modelfile: `FROM test TEMPLATE """{{- if .Suffix }}
 {{ .Prompt }} {{ .Suffix }} 
diff --git a/server/routes_list_test.go b/server/routes_list_test.go
index 6e92b7a1..56b40830 100644
--- a/server/routes_list_test.go
+++ b/server/routes_list_test.go
@@ -31,13 +31,13 @@ func TestList(t *testing.T) {
 
 	var s Server
 	for _, n := range expectNames {
-		createRequest(t, s.CreateModelHandler, api.CreateRequest{
+		createRequest(t, s.CreateHandler, api.CreateRequest{
 			Name:      n,
 			Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)),
 		})
 	}
 
-	w := createRequest(t, s.ListModelsHandler, nil)
+	w := createRequest(t, s.ListHandler, nil)
 	if w.Code != http.StatusOK {
 		t.Fatalf("expected status code 200, actual %d", w.Code)
 	}
diff --git a/server/routes_test.go b/server/routes_test.go
index 242875d6..bffcea20 100644
--- a/server/routes_test.go
+++ b/server/routes_test.go
@@ -318,7 +318,7 @@ func TestCase(t *testing.T) {
 	var s Server
 	for _, tt := range cases {
 		t.Run(tt, func(t *testing.T) {
-			w := createRequest(t, s.CreateModelHandler, api.CreateRequest{
+			w := createRequest(t, s.CreateHandler, api.CreateRequest{
 				Name:      tt,
 				Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)),
 				Stream:    &stream,
@@ -334,7 +334,7 @@ func TestCase(t *testing.T) {
 			}
 
 			t.Run("create", func(t *testing.T) {
-				w = createRequest(t, s.CreateModelHandler, api.CreateRequest{
+				w = createRequest(t, s.CreateHandler, api.CreateRequest{
 					Name:      strings.ToUpper(tt),
 					Modelfile: fmt.Sprintf("FROM %s", createBinFile(t, nil, nil)),
 					Stream:    &stream,
@@ -350,7 +350,7 @@ func TestCase(t *testing.T) {
 			})
 
 			t.Run("pull", func(t *testing.T) {
-				w := createRequest(t, s.PullModelHandler, api.PullRequest{
+				w := createRequest(t, s.PullHandler, api.PullRequest{
 					Name:   strings.ToUpper(tt),
 					Stream: &stream,
 				})
@@ -365,7 +365,7 @@ func TestCase(t *testing.T) {
 			})
 
 			t.Run("copy", func(t *testing.T) {
-				w := createRequest(t, s.CopyModelHandler, api.CopyRequest{
+				w := createRequest(t, s.CopyHandler, api.CopyRequest{
 					Source:      tt,
 					Destination: strings.ToUpper(tt),
 				})
@@ -387,7 +387,7 @@ func TestShow(t *testing.T) {
 
 	var s Server
 
-	createRequest(t, s.CreateModelHandler, api.CreateRequest{
+	createRequest(t, s.CreateHandler, api.CreateRequest{
 		Name: "show-model",
 		Modelfile: fmt.Sprintf(
 			"FROM %s\nFROM %s",
@@ -396,7 +396,7 @@ func TestShow(t *testing.T) {
 		),
 	})
 
-	w := createRequest(t, s.ShowModelHandler, api.ShowRequest{
+	w := createRequest(t, s.ShowHandler, api.ShowRequest{
 		Name: "show-model",
 	})
 

From ac80010db8ceebac6891beea9a5cd761815678ad Mon Sep 17 00:00:00 2001
From: Patrick Devine 
Date: Mon, 26 Aug 2024 19:57:26 -0700
Subject: [PATCH 073/174] update the import docs (#6104)

---
 docs/images/ollama-keys.png | Bin 0 -> 144101 bytes
 docs/images/signup.png      | Bin 0 -> 81360 bytes
 docs/import.md              | 188 +++++++++++++++++++++++++++---------
 3 files changed, 142 insertions(+), 46 deletions(-)
 create mode 100644 docs/images/ollama-keys.png
 create mode 100644 docs/images/signup.png

diff --git a/docs/images/ollama-keys.png b/docs/images/ollama-keys.png
new file mode 100644
index 0000000000000000000000000000000000000000..119a4bcf4cb0cf0639f3e71e3cb70010f8179316
GIT binary patch
literal 144101
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
E3ov}7S^xk5

literal 0
HcmV?d00001

diff --git a/docs/import.md b/docs/import.md
index 82ea9ba5..baf34550 100644
--- a/docs/import.md
+++ b/docs/import.md
@@ -1,44 +1,129 @@
-# Import
+# Importing a model
 
-GGUF models and select Safetensors models can be imported directly into Ollama.
+## Table of Contents
 
-## Import GGUF
+  * [Importing a Safetensors adapter](#Importing-a-fine-tuned-adapter-from-Safetensors-weights)
+  * [Importing a Safetensors model](#Importing-a-model-from-Safetensors-weights)
+  * [Importing a GGUF file](#Importing-a-GGUF-based-model-or-adapter)
+  * [Sharing models on ollama.com](#Sharing-your-model-on-ollama.com)
 
-A binary GGUF file can be imported directly into Ollama through a Modelfile.
+## Importing a fine tuned adapter from Safetensors weights
+
+First, create a `Modelfile` with a `FROM` command pointing at the base model you used for fine tuning, and an `ADAPTER` command which points to the directory with your Safetensors adapter:
 
 ```dockerfile
-FROM /path/to/file.gguf
+FROM 
+ADAPTER /path/to/safetensors/adapter/directory
 ```
 
-## Import Safetensors
+Make sure that you use the same base model in the `FROM` command as you used to create the adapter otherwise you will get erratic results. Most frameworks use different quantization methods, so it's best to use non-quantized (i.e. non-QLoRA) adapters. If your adapter is in the same directory as your `Modelfile`, use `ADAPTER .` to specify the adapter path.
 
-If the model being imported is one of these architectures, it can be imported directly into Ollama through a Modelfile:
+Now run `ollama create` from the directory where the `Modelfile` was created:
 
- - LlamaForCausalLM
- - MistralForCausalLM
- - MixtralForCausalLM
- - GemmaForCausalLM
- - Phi3ForCausalLM
+```bash
+ollama create my-model
+```
+
+Lastly, test the model:
+
+```bash
+ollama run my-model
+```
+
+Ollama supports importing adapters based on several different model architectures including:
+
+  * Llama (including Llama 2, Llama 3, and Llama 3.1);
+  * Mistral (including Mistral 1, Mistral 2, and Mixtral); and
+  * Gemma (including Gemma 1 and Gemma 2)
+
+You can create the adapter using a fine tuning framework or tool which can output adapters in the Safetensors format, such as:
+
+  * Hugging Face [fine tuning framework] (https://huggingface.co/docs/transformers/en/training)
+  * [Unsloth](https://github.com/unslothai/unsloth)
+  * [MLX](https://github.com/ml-explore/mlx)
+
+
+## Importing a model from Safetensors weights
+
+First, create a `Modelfile` with a `FROM` command which points to the directory containing your Safetensors weights:
 
 ```dockerfile
 FROM /path/to/safetensors/directory
 ```
 
-For architectures not directly convertable by Ollama, see llama.cpp's [guide](https://github.com/ggerganov/llama.cpp/blob/master/README.md#prepare-and-quantize) on conversion. After conversion, see [Import GGUF](#import-gguf).
+If you create the Modelfile in the same directory as the weights, you can use the command `FROM .`.
 
-## Automatic Quantization
+Now run the `ollama create` command from the directory where you created the `Modelfile`:
 
-> [!NOTE]
-> Automatic quantization requires v0.1.35 or higher.
+```shell
+ollama create my-model
+```
 
-Ollama is capable of quantizing FP16 or FP32 models to any of the supported quantizations with the `-q/--quantize` flag in `ollama create`.
+Lastly, test the model:
+
+```shell
+ollama run my-model
+```
+
+Ollama supports importing models for several different architectures including:
+
+  * Llama (including Llama 2, Llama 3, and Llama 3.1);
+  * Mistral (including Mistral 1, Mistral 2, and Mixtral);
+  * Gemma (including Gemma 1 and Gemma 2); and
+  * Phi3
+
+This includes importing foundation models as well as any fine tuned models which which have been _fused_ with a foundation model.
+
+
+## Importing a GGUF based model or adapter
+
+If you have a GGUF based model or adapter it is possible to import it into Ollama. You can obtain a GGUF model or adapter by:
+
+  * converting a Safetensors model with the `convert_hf_to_gguf.py` from Llama.cpp; 
+  * converting a Safetensors adapter with the `convert_lora_to_gguf.py` from Llama.cpp; or
+  * downloading a model or adapter from a place such as HuggingFace
+
+To import a GGUF model, create a `Modelfile` containg:
+
+```dockerfile
+FROM /path/to/file.gguf
+```
+
+For a GGUF adapter, create the `Modelfile` with:
+
+```dockerfile
+FROM 
+ADAPTER /path/to/file.gguf
+```
+
+When importing a GGUF adapter, it's important to use the same base model as the base model that the adapter was created with. You can use:
+
+ * a model from Ollama
+ * a GGUF file
+ * a Safetensors based model 
+
+Once you have created your `Modelfile`, use the `ollama create` command to build the model.
+
+```shell
+ollama create my-model
+```
+
+## Quantizing a Model
+
+Quantizing a model allows you to run models faster and with less memory consumption but at reduced accuracy. This allows you to run a model on more modest hardware.
+
+Ollama can quantize FP16 and FP32 based models into different quantization levels using the `-q/--quantize` flag with the `ollama create` command.
+
+First, create a Modelfile with the FP16 or FP32 based model you wish to quantize.
 
 ```dockerfile
 FROM /path/to/my/gemma/f16/model
 ```
 
+Use `ollama create` to then create the quantized model.
+
 ```shell
-$ ollama create -q Q4_K_M mymodel
+$ ollama create --quantize q4_K_M mymodel
 transferring model data
 quantizing F16 model to Q4_K_M
 creating new layer sha256:735e246cc1abfd06e9cdcf95504d6789a6cd1ad7577108a70d9902fef503c1bd
@@ -49,42 +134,53 @@ success
 
 ### Supported Quantizations
 
-- `Q4_0`
-- `Q4_1`
-- `Q5_0`
-- `Q5_1`
-- `Q8_0`
+- `q4_0`
+- `q4_1`
+- `q5_0`
+- `q5_1`
+- `q8_0`
 
 #### K-means Quantizations
 
-- `Q3_K_S`
-- `Q3_K_M`
-- `Q3_K_L`
-- `Q4_K_S`
-- `Q4_K_M`
-- `Q5_K_S`
-- `Q5_K_M`
-- `Q6_K`
+- `q3_K_S`
+- `q3_K_M`
+- `q3_K_L`
+- `q4_K_S`
+- `q4_K_M`
+- `q5_K_S`
+- `q5_K_M`
+- `q6_K`
 
-## Template Detection
 
-> [!NOTE]
-> Template detection requires v0.1.42 or higher.
+## Sharing your model on ollama.com
 
-Ollama uses model metadata, specifically `tokenizer.chat_template`, to automatically create a template appropriate for the model you're importing.
+You can share any model you have created by pushing it to [ollama.com](https://ollama.com) so that other users can try it out.
 
-```dockerfile
-FROM /path/to/my/gemma/model
-```
+First, use your browser to go to the [Ollama Sign-Up](https://ollama.com/signup) page. If you already have an account, you can skip this step.
+
+![Sign-Up](images/signup.png)
+
+The `Username` field will be used as part of your model's name (e.g. `jmorganca/mymodel`), so make sure you are comfortable with the username that you have selected.
+
+Now that you have created an account and are signed-in, go to the [Ollama Keys Settings](https://ollama.com/settings/keys) page.
+
+Follow the directions on the page to determine where your Ollama Public Key is located.
+
+![Ollama Key](images/ollama-keys.png)
+
+Click on the `Add Ollama Public Key` button, and copy and paste the contents of your Ollama Public Key into the text field.
+
+To push a model to [ollama.com](https://ollama.com), first make sure that it is named correctly with your username. You may have to use the `ollama cp` command to copy
+your model to give it the correct name. Once you're happy with your model's name, use the `ollama push` command to push it to [ollama.com](https://ollama.com).
 
 ```shell
-$ ollama create mymodel
-transferring model data
-using autodetected template gemma-instruct
-creating new layer sha256:baa2a0edc27d19cc6b7537578a9a7ba1a4e3214dc185ed5ae43692b319af7b84
-creating new layer sha256:ba66c3309914dbef07e5149a648fd1877f030d337a4f240d444ea335008943cb
-writing manifest
-success
+ollama cp mymodel myuser/mymodel
+ollama push myuser/mymodel
+```
+
+Once your model has been pushed, other users can pull and run it by using the command:
+
+```shell
+ollama run myuser/mymodel
 ```
 
-Defining a template in the Modelfile will disable this feature which may be useful if you want to use a different template than the autodetected one.

From 3eb08377f82a7df091df5a9e6e73fac0ed3bcc26 Mon Sep 17 00:00:00 2001
From: Michael Yang 
Date: Mon, 26 Aug 2024 16:36:50 -0700
Subject: [PATCH 074/174] detect chat template from configs that contain lists

---
 convert/tokenizer.go      | 17 ++++++-
 convert/tokenizer_test.go | 96 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 2 deletions(-)
 create mode 100644 convert/tokenizer_test.go

diff --git a/convert/tokenizer.go b/convert/tokenizer.go
index 653df6d2..429d36e7 100644
--- a/convert/tokenizer.go
+++ b/convert/tokenizer.go
@@ -100,8 +100,21 @@ func parseTokenizer(fsys fs.FS, specialTokenTypes []string) (*Tokenizer, error)
 		}
 
 		if template, ok := p["chat_template"]; ok {
-			if err := json.Unmarshal(template, &t.Template); err != nil {
-				return nil, err
+			var s []struct {
+				Name     string `json:"name"`
+				Template string `json:"template"`
+			}
+			if err := json.Unmarshal(template, &t.Template); err == nil {
+				// noop
+			} else if err := json.Unmarshal(template, &s); err == nil {
+				for _, e := range s {
+					if e.Name == "default" {
+						t.Template = e.Template
+						break
+					}
+				}
+			} else {
+				return nil, fmt.Errorf("invalid chat_template: %w", err)
 			}
 		}
 
diff --git a/convert/tokenizer_test.go b/convert/tokenizer_test.go
new file mode 100644
index 00000000..ed0175a4
--- /dev/null
+++ b/convert/tokenizer_test.go
@@ -0,0 +1,96 @@
+package convert
+
+import (
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func createTokenizerFS(t *testing.T, dir string, files map[string]io.Reader) fs.FS {
+	t.Helper()
+
+	for k, v := range files {
+		if err := func() error {
+			f, err := os.Create(filepath.Join(dir, k))
+			if err != nil {
+				return err
+			}
+			defer f.Close()
+
+			if _, err := io.Copy(f, v); err != nil {
+				return err
+			}
+
+			return nil
+		}(); err != nil {
+			t.Fatalf("unexpected error: %v", err)
+		}
+	}
+
+	return os.DirFS(dir)
+}
+
+func TestParseTokenizer(t *testing.T) {
+	cases := []struct {
+		name              string
+		fsys              fs.FS
+		specialTokenTypes []string
+		want              *Tokenizer
+	}{
+		{
+			name: "string chat template",
+			fsys: createTokenizerFS(t, t.TempDir(), map[string]io.Reader{
+				"tokenizer.json": strings.NewReader(`{}`),
+				"tokenizer_config.json": strings.NewReader(`{
+					"chat_template": ""
+				}`),
+			}),
+			want: &Tokenizer{
+				Vocabulary: &Vocabulary{Model: "gpt2"},
+				Pre:        "default",
+				Template:   "",
+			},
+		},
+		{
+			name: "list chat template",
+			fsys: createTokenizerFS(t, t.TempDir(), map[string]io.Reader{
+				"tokenizer.json": strings.NewReader(`{}`),
+				"tokenizer_config.json": strings.NewReader(`{
+					"chat_template": [
+						{
+							"name": "default",
+							"template": ""
+						},
+						{
+							"name": "tools",
+							"template": ""
+						}
+					]
+				}`),
+			}),
+			want: &Tokenizer{
+				Vocabulary: &Vocabulary{Model: "gpt2"},
+				Pre:        "default",
+				Template:   "",
+			},
+		},
+	}
+
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			tokenizer, err := parseTokenizer(tt.fsys, tt.specialTokenTypes)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+
+			if diff := cmp.Diff(tt.want, tokenizer); diff != "" {
+				t.Errorf("unexpected tokenizer (-want +got):\n%s", diff)
+			}
+		})
+	}
+}

From eae3af6807954c10f7a95c4c1bfb536330270a23 Mon Sep 17 00:00:00 2001
From: Michael Yang 
Date: Tue, 27 Aug 2024 10:45:39 -0700
Subject: [PATCH 075/174] clean up convert tokenizer

---
 convert/convert_test.go | 2 +-
 convert/tokenizer.go    | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/convert/convert_test.go b/convert/convert_test.go
index 56b34f22..7dd489e2 100644
--- a/convert/convert_test.go
+++ b/convert/convert_test.go
@@ -89,7 +89,7 @@ func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
 
-func TestConvertFull(t *testing.T) {
+func TestConvertModel(t *testing.T) {
 	cases := []string{
 		"Meta-Llama-3-8B-Instruct",
 		"Meta-Llama-3.1-8B-Instruct",
diff --git a/convert/tokenizer.go b/convert/tokenizer.go
index 429d36e7..14d6ba66 100644
--- a/convert/tokenizer.go
+++ b/convert/tokenizer.go
@@ -154,7 +154,6 @@ func parseTokenizer(fsys fs.FS, specialTokenTypes []string) (*Tokenizer, error)
 }
 
 type tokenizer struct {
-	Version     string  `json:"version"`
 	AddedTokens []token `json:"added_tokens"`
 	Model       struct {
 		Type   string         `json:"type"`
@@ -252,7 +251,7 @@ func parseVocabulary(fsys fs.FS) (*Vocabulary, error) {
 		return pattern.Func(fsys)
 	}
 
-	return nil, errors.New("unknown tensor format")
+	return nil, errors.New("unknown tokenizer format")
 }
 
 type SpecialVocabulary struct {

From 1c70a00f716ed61c5b0a9e0f2a01876de0fc54d0 Mon Sep 17 00:00:00 2001
From: Patrick Devine 
Date: Tue, 27 Aug 2024 11:15:25 -0700
Subject: [PATCH 076/174] adjust image sizes

---
 docs/import.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/import.md b/docs/import.md
index baf34550..a81c4bb4 100644
--- a/docs/import.md
+++ b/docs/import.md
@@ -158,7 +158,7 @@ You can share any model you have created by pushing it to [ollama.com](https://o
 
 First, use your browser to go to the [Ollama Sign-Up](https://ollama.com/signup) page. If you already have an account, you can skip this step.
 
-![Sign-Up](images/signup.png)
+Sign-Up
 
 The `Username` field will be used as part of your model's name (e.g. `jmorganca/mymodel`), so make sure you are comfortable with the username that you have selected.
 
@@ -166,7 +166,7 @@ Now that you have created an account and are signed-in, go to the [Ollama Keys S
 
 Follow the directions on the page to determine where your Ollama Public Key is located.
 
-![Ollama Key](images/ollama-keys.png)
+Ollama Keys
 
 Click on the `Add Ollama Public Key` button, and copy and paste the contents of your Ollama Public Key into the text field.
 

From 397cae79620e09220f4207beadd523ce1ae7cbd5 Mon Sep 17 00:00:00 2001
From: Sean Khatiri <39913795+seankhatiri@users.noreply.github.com>
Date: Tue, 27 Aug 2024 16:28:29 -0400
Subject: [PATCH 077/174] llm: fix typo in comment (#6530)

---
 llm/server.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llm/server.go b/llm/server.go
index 4e5dac28..c38bc6bb 100644
--- a/llm/server.go
+++ b/llm/server.go
@@ -409,7 +409,7 @@ func NewLlamaServer(gpus gpu.GpuInfoList, model string, ggml *GGML, adapters, pr
 		}
 
 		if err = s.cmd.Start(); err != nil {
-			// Detect permission denied and augment them essage about noexec
+			// Detect permission denied and augment the message about noexec
 			if errors.Is(err, os.ErrPermission) {
 				finalErr = fmt.Errorf("unable to start server %w.  %s may have noexec set.  Set OLLAMA_TMPDIR for server to a writable executable directory", err, dir)
 				continue

From 4e1c4f6e0bfc7b0feabe08d7aa4bf92f444fbe66 Mon Sep 17 00:00:00 2001
From: Daniel Hiltgen 
Date: Tue, 27 Aug 2024 13:42:28 -0700
Subject: [PATCH 078/174] Update manual instructions with discrete ROCm bundle
 (#6445)

---
 docs/linux.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/docs/linux.md b/docs/linux.md
index d1d5892c..fbaf4845 100644
--- a/docs/linux.md
+++ b/docs/linux.md
@@ -28,6 +28,11 @@ Download and extract the Linux package:
 curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | sudo tar zx -C /usr
 ```
 
+If you have an AMD GPU, also download and extract the ROCm package into the same location
+```bash
+curl -fsSL https://ollama.com/download/ollama-linux-amd64-rocm.tgz | sudo tar zx -C /usr
+```
+
 ### Adding Ollama as a startup service (recommended)
 
 Create a user for Ollama:

From 1713eddcd05e4c234108511f6fc1470c383e21b2 Mon Sep 17 00:00:00 2001
From: Patrick Devine 
Date: Tue, 27 Aug 2024 14:19:47 -0700
Subject: [PATCH 079/174] Fix import image width (#6528)

---
 docs/images/ollama-keys.png | Bin 144101 -> 154049 bytes
 docs/import.md              |   6 +++---
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/images/ollama-keys.png b/docs/images/ollama-keys.png
index 119a4bcf4cb0cf0639f3e71e3cb70010f8179316..942079a8637c6e1312c30f19a688e115fda93829 100644
GIT binary patch
literal 154049
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

!pwP8H(-MhL|l_2v~BpuT+LXgK7$OKKh;u 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 TE)4q`@J~ZkSEW+PBKUs+^5v58 literal 144101 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 +Sign-Up The `Username` field will be used as part of your model's name (e.g. `jmorganca/mymodel`), so make sure you are comfortable with the username that you have selected. @@ -166,7 +166,7 @@ Now that you have created an account and are signed-in, go to the [Ollama Keys S Follow the directions on the page to determine where your Ollama Public Key is located. -Ollama Keys +Ollama Keys Click on the `Add Ollama Public Key` button, and copy and paste the contents of your Ollama Public Key into the text field. From d13c3daa0bff581532b29c269040338fdc895aeb Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Tue, 27 Aug 2024 14:46:47 -0700 Subject: [PATCH 080/174] add safetensors to the modelfile docs (#6532) --- docs/modelfile.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/modelfile.md b/docs/modelfile.md index 852bf96c..51827e74 100644 --- a/docs/modelfile.md +++ b/docs/modelfile.md @@ -11,8 +11,9 @@ A model file is the blueprint to create and share models with Ollama. - [Examples](#examples) - [Instructions](#instructions) - [FROM (Required)](#from-required) - - [Build from llama3](#build-from-llama3) - - [Build from a bin file](#build-from-a-bin-file) + - [Build from llama3.1](#build-from-llama31) + - [Build from a Safetensors model](#build-from-a-safetensors-model) + - [Build from a GGUF file](#build-from-a-gguf-file) - [PARAMETER](#parameter) - [Valid Parameters and Values](#valid-parameters-and-values) - [TEMPLATE](#template) @@ -99,22 +100,39 @@ The `FROM` instruction defines the base model to use when creating a model. FROM : ``` -#### Build from llama3 +#### Build from llama3.1 ```modelfile -FROM llama3 +FROM llama3.1 ``` A list of available base models: +Additional models can be found at: + -#### Build from a `bin` file +#### Build from a Safetensors model + +```modelfile +FROM +``` + +The model directory should contain the Safetensors weights for a supported architecture. + +Currently supported model architectures: + * Llama (including Llama 2, Llama 3, and Llama 3.1) + * Mistral (including Mistral 1, Mistral 2, and Mixtral) + * Gemma (including Gemma 1 and Gemma 2) + * Phi3 + +#### Build from a GGUF file ```modelfile FROM ./ollama-model.bin ``` -This bin file location should be specified as an absolute path or relative to the `Modelfile` location. +The GGUF bin file location should be specified as an absolute path or relative to the `Modelfile` location. + ### PARAMETER @@ -174,7 +192,20 @@ SYSTEM """""" ### ADAPTER -The `ADAPTER` instruction is an optional instruction that specifies any LoRA adapter that should apply to the base model. The value of this instruction should be an absolute path or a path relative to the Modelfile and the file must be in a GGML file format. The adapter should be tuned from the base model otherwise the behaviour is undefined. +The `ADAPTER` instruction specifies a fine tuned LoRA adapter that should apply to the base model. The value of the adapter should be an absolute path or a path relative to the Modelfile. The base model should be specified with a `FROM` instruction. If the base model is not the same as the base model that the adapter was tuned from the behaviour will be erratic. + +#### Safetensor adapter + +```modelfile +ADAPTER +``` + +Currently supported Safetensor adapters: + * Llama (including Llama 2, Llama 3, and Llama 3.1) + * Mistral (including Mistral 1, Mistral 2, and Mixtral) + * Gemma (including Gemma 1 and Gemma 2) + +#### GGUF adapter ```modelfile ADAPTER ./ollama-lora.bin From 60e47573a6efef0c9d13a2970a3951d739b9304e Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 27 Aug 2024 11:11:53 -0700 Subject: [PATCH 081/174] more tokenizer tests --- convert/tokenizer_test.go | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/convert/tokenizer_test.go b/convert/tokenizer_test.go index ed0175a4..d9550e09 100644 --- a/convert/tokenizer_test.go +++ b/convert/tokenizer_test.go @@ -79,6 +79,118 @@ func TestParseTokenizer(t *testing.T) { Template: "", }, }, + { + name: "added tokens", + fsys: createTokenizerFS(t, t.TempDir(), map[string]io.Reader{ + "tokenizer.json": strings.NewReader(`{ + "added_tokens": [ + { + "id": 999, + "content": "", + "special": false + } + ] + }`), + }), + want: &Tokenizer{ + Vocabulary: &Vocabulary{ + Model: "gpt2", + Tokens: []string{""}, + Scores: []float32{999}, + Types: []int32{4}, + }, + Pre: "default", + }, + }, + { + name: "added tokens overlap vocab", + fsys: createTokenizerFS(t, t.TempDir(), map[string]io.Reader{ + "tokenizer.json": strings.NewReader(`{ + "added_tokens": [ + { + "id": 0, + "content": "", + "special": true + } + ], + "model": { + "vocab": { + "": 0 + } + } + }`), + }), + want: &Tokenizer{ + Vocabulary: &Vocabulary{ + Model: "gpt2", + Tokens: []string{""}, + Scores: []float32{0}, + Types: []int32{3}, + }, + Pre: "default", + }, + }, + { + name: "special token types", + fsys: createTokenizerFS(t, t.TempDir(), map[string]io.Reader{ + "tokenizer.json": strings.NewReader(`{ + "added_tokens": [ + { + "id": 0, + "content": "", + "special": true + }, + { + "id": 1, + "content": "", + "special": true + }, + { + "id": 2, + "content": "", + "special": true + }, + { + "id": 3, + "content": "", + "special": true + } + ], + "model": { + "vocab": { + "": 0, + "": 1, + "": 2, + "": 3 + } + } + }`), + "tokenizer_config.json": strings.NewReader(`{ + "add_bos_token": true, + "add_eos_token": false, + "bos_token": "", + "eos_token": "", + "pad_token": "", + "unk_token": "" + }`), + }), + specialTokenTypes: []string{"pad", "eos", "bos", "unk"}, + want: &Tokenizer{ + Vocabulary: &Vocabulary{ + Model: "gpt2", + Tokens: []string{"", "", "", ""}, + Scores: []float32{0, 1, 2, 3}, + Types: []int32{3, 3, 3, 3}, + }, + SpecialVocabulary: []*SpecialVocabulary{ + {Type: "pad", Content: "", ID: 0, AddToken: false}, + {Type: "eos", Content: "", ID: 1, AddToken: false}, + {Type: "bos", Content: "", ID: 2, AddToken: true}, + {Type: "unk", Content: "", ID: 3, AddToken: false}, + }, + Pre: "default", + }, + }, } for _, tt := range cases { From 413ae39f3c947a65f9c940edc0319218135ba767 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 27 Aug 2024 11:34:30 -0700 Subject: [PATCH 082/174] update templates to use messages --- server/routes_create_test.go | 4 ++-- template/alfred.gotmpl | 3 ++- template/alpaca.gotmpl | 20 +++++++++++++++----- template/chatml.gotmpl | 7 ++----- template/chatqa.gotmpl | 11 ++++++----- template/codellama-70b-instruct.gotmpl | 16 ++++++++-------- template/falcon-instruct.gotmpl | 11 +++++++---- template/gemma-instruct.gotmpl | 21 ++++++++++++++++----- template/granite-instruct.gotmpl | 13 ++++++------- template/llama2-chat.gotmpl | 18 +++++++++++++----- template/llama3-instruct.gotmpl | 8 +++----- template/magicoder.gotmpl | 19 ++++++++++++++----- template/mistral-instruct.gotmpl | 7 +++++-- template/openchat.gotmpl | 7 ++++++- template/phi-3.gotmpl | 7 ++----- template/solar-instruct.gotmpl | 14 ++++++++------ template/starcoder2-instruct.gotmpl | 20 +++++++++++++++----- template/vicuna.gotmpl | 16 +++++++++++++--- template/zephyr.gotmpl | 7 ++----- 19 files changed, 145 insertions(+), 84 deletions(-) diff --git a/server/routes_create_test.go b/server/routes_create_test.go index 4de07b25..2f577eb4 100644 --- a/server/routes_create_test.go +++ b/server/routes_create_test.go @@ -593,9 +593,9 @@ func TestCreateDetectTemplate(t *testing.T) { checkFileExists(t, filepath.Join(p, "blobs", "*"), []string{ filepath.Join(p, "blobs", "sha256-0d79f567714c62c048378f2107fb332dabee0135d080c302d884317da9433cc5"), + filepath.Join(p, "blobs", "sha256-35360843d0c84fb1506952a131bbef13cd2bb4a541251f22535170c05b56e672"), filepath.Join(p, "blobs", "sha256-553c4a3f747b3d22a4946875f1cc8ed011c2930d83f864a0c7265f9ec0a20413"), - filepath.Join(p, "blobs", "sha256-c608dc615584cd20d9d830363dabf8a4783ae5d34245c3d8c115edb3bc7b28e4"), - filepath.Join(p, "blobs", "sha256-ea34c57ba5b78b740aafe2aeb74dc6507fc3ad14170b64c26a04fb9e36c88d75"), + filepath.Join(p, "blobs", "sha256-de3959f841e9ef6b4b6255fa41cb9e0a45da89c3066aa72bdd07a4747f848990"), }) }) diff --git a/template/alfred.gotmpl b/template/alfred.gotmpl index cecb9d2c..86dba48f 100644 --- a/template/alfred.gotmpl +++ b/template/alfred.gotmpl @@ -1 +1,2 @@ -{{ if .System }}{{ .System }}{{ end }}{{ if .Prompt }}{{ .Prompt }}{{ end }}{{ .Response }} \ No newline at end of file +{{- range .Messages }}{{ .Content }} +{{- end }} \ No newline at end of file diff --git a/template/alpaca.gotmpl b/template/alpaca.gotmpl index ec7a8edc..00439736 100644 --- a/template/alpaca.gotmpl +++ b/template/alpaca.gotmpl @@ -1,8 +1,18 @@ -{{ if .System }}{{ .System }} +{{- $system := "" }} +{{- range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- else if eq .Role "user" }} +{{- if $system }}{{ $system }} -{{ end }}{{ if .Prompt }}### Instruction: -{{ .Prompt }} +{{ $system = "" }} +{{- end }}### Instruction: +{{ .Content }} -{{ end }}### Response: -{{ .Response }} +{{ else if eq .Role "assistant" }}### Response: +{{ .Content }} +{{ end }} +{{- end }}### Response: diff --git a/template/chatml.gotmpl b/template/chatml.gotmpl index fb672601..43207ab1 100644 --- a/template/chatml.gotmpl +++ b/template/chatml.gotmpl @@ -1,6 +1,3 @@ -{{ if .System }}<|im_start|>system -{{ .System }}<|im_end|> -{{ end }}{{ if .Prompt }}<|im_start|>user -{{ .Prompt }}<|im_end|> +{{- range .Messages }}<|im_start|>{{ .Role }} +{{ .Content }}<|im_end|> {{ end }}<|im_start|>assistant -{{ .Response }}<|im_end|> diff --git a/template/chatqa.gotmpl b/template/chatqa.gotmpl index 91679a72..0f91e0f0 100644 --- a/template/chatqa.gotmpl +++ b/template/chatqa.gotmpl @@ -1,6 +1,7 @@ -{{ if .System }}System: {{ .System }} - -{{ end }}{{ if .Prompt }}User: {{ .Prompt }} - -{{ end }}Assistant: {{ .Response }} +{{- range .Messages }} +{{- if eq .Role "system" }}System: +{{- else if eq .Role "user" }}User: +{{- else if eq .Role "assistant" }}Assistant: +{{- end }} {{ .Content }} +{{ end }}Assistant: \ No newline at end of file diff --git a/template/codellama-70b-instruct.gotmpl b/template/codellama-70b-instruct.gotmpl index e5856042..18931520 100644 --- a/template/codellama-70b-instruct.gotmpl +++ b/template/codellama-70b-instruct.gotmpl @@ -1,10 +1,10 @@ -{{ if .System }}Source: system - - {{ .System }} {{ end }}Source: user - - {{ .Prompt }} Source: assistant -{{- if not .Response }} -Destination: user +{{- range .Messages }}Source: +{{- if eq .Role "system" }} system +{{- else if eq .Role "user" }} user +{{- else if eq .Role "assistant" }} assistant {{- end }} - {{ .Response }} \ No newline at end of file + {{ .Content }} {{ end }}Source: assistant +Destination: user + + \ No newline at end of file diff --git a/template/falcon-instruct.gotmpl b/template/falcon-instruct.gotmpl index 0a5fe48e..b9b51d2c 100644 --- a/template/falcon-instruct.gotmpl +++ b/template/falcon-instruct.gotmpl @@ -1,5 +1,8 @@ -{{ if .System }}System: {{ .System }} -{{ end }}{{ if .Prompt }}User: -{{ .Prompt }} +{{- range .Messages }} +{{- if eq .Role "system" }}System: {{ .Content }} +{{ continue }} +{{- else if eq .Role "user" }}User: +{{- else if eq .Role "assistant" }}Falcon: +{{- end }} +{{ .Content }} {{ end }}Falcon: -{{ .Response }} diff --git a/template/gemma-instruct.gotmpl b/template/gemma-instruct.gotmpl index 3c3a8425..cce25719 100644 --- a/template/gemma-instruct.gotmpl +++ b/template/gemma-instruct.gotmpl @@ -1,5 +1,16 @@ -user -{{ if .System }}{{ .System }} -{{ end }}{{ .Prompt }} -model -{{ .Response }} +{{- $system := "" }} +{{- range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- continue }} +{{- else if eq .Role "user" }}user +{{- if $system }} +{{ $system }} +{{- $system = "" }} +{{- end }} +{{- else if eq .Role "assistant" }}model +{{- end }} +{{ .Content }} +{{ end }}model diff --git a/template/granite-instruct.gotmpl b/template/granite-instruct.gotmpl index 56690fce..83634990 100644 --- a/template/granite-instruct.gotmpl +++ b/template/granite-instruct.gotmpl @@ -1,9 +1,8 @@ -{{ if .System }}System: -{{ .System }} - -{{ end }}{{ if .Prompt }}Question: -{{ .Prompt }} +{{- range .Messages }} +{{- if eq .Role "system" }}System: +{{- else if eq .Role "user" }}Question: +{{- else if eq .Role "assistant" }}Answer: +{{- end }} +{{ .Content }} {{ end }}Answer: -{{ .Response }} - diff --git a/template/llama2-chat.gotmpl b/template/llama2-chat.gotmpl index 013b414e..5634a072 100644 --- a/template/llama2-chat.gotmpl +++ b/template/llama2-chat.gotmpl @@ -1,6 +1,14 @@ -[INST] <> -{{- if .System }} -{{ .System }} -{{ end }}<> +{{- $system := "" }}[INST] {{ range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- else if eq .Role "user" }}<> +{{- if $system }} +{{ $system }} +{{ $system = "" }} +{{- end }}<> -{{ .Prompt }} [/INST] {{ .Response }} \ No newline at end of file +{{ .Content }} [/INST] +{{- else if eq .Role "assistant" }} {{ .Content }}[INST] {{ end }} +{{- end }} \ No newline at end of file diff --git a/template/llama3-instruct.gotmpl b/template/llama3-instruct.gotmpl index 36d0218b..305ae403 100644 --- a/template/llama3-instruct.gotmpl +++ b/template/llama3-instruct.gotmpl @@ -1,7 +1,5 @@ -{{ if .System }}<|start_header_id|>system<|end_header_id|> +{{- range .Messages }}<|start_header_id|>{{ .Role }}<|end_header_id|> -{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|> +{{ .Content }}<|eot_id|> +{{- end }}<|start_header_id|>assistant<|end_header_id|> -{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|> - -{{ .Response }}<|eot_id|> \ No newline at end of file diff --git a/template/magicoder.gotmpl b/template/magicoder.gotmpl index 52abc01a..e5ee0e47 100644 --- a/template/magicoder.gotmpl +++ b/template/magicoder.gotmpl @@ -1,8 +1,17 @@ -{{ if .System }}{{ .System }} +{{- $system := "" }} +{{- range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- continue }} +{{- else if eq .Role "user" }} +{{- if $system }}{{ $system }} -{{ end }}{{ if .Prompt }}@@ Instruction -{{ .Prompt }} +{{ $system = "" }} +{{- end }}@@ Instruction +{{- else if eq .Role "assistant" }}@@ Response +{{- end }} +{{ .Content }} {{ end }}@@ Response -{{ .Response }} - diff --git a/template/mistral-instruct.gotmpl b/template/mistral-instruct.gotmpl index e489bd4c..7a6ecdfd 100644 --- a/template/mistral-instruct.gotmpl +++ b/template/mistral-instruct.gotmpl @@ -1,3 +1,6 @@ -[INST] {{ if .System }}{{ .System }} +[INST] {{ range $index, $_ := .Messages }} +{{- if eq .Role "system" }}{{ .Content }} -{{ end }}{{ .Prompt }}[/INST] {{ .Response }} \ No newline at end of file +{{ else if eq .Role "user" }}{{ .Content }}[/INST] +{{- else if eq .Role "assistant" }} {{ .Content }}[INST] {{ end }} +{{- end }} \ No newline at end of file diff --git a/template/openchat.gotmpl b/template/openchat.gotmpl index 9c183834..66a4d687 100644 --- a/template/openchat.gotmpl +++ b/template/openchat.gotmpl @@ -1 +1,6 @@ -{{ if .System }}GPT4 Correct System: {{ .System }}<|end_of_turn|>{{ end }}GPT4 Correct User: {{ .Prompt }}<|end_of_turn|>GPT4 Correct Assistant: {{ .Response }}<|end_of_turn|> \ No newline at end of file +{{- range .Messages }}GPT4 Correct +{{- if eq .Role "system" }} System: +{{- else if eq .Role "user" }} User: +{{- else if eq .Role "assistant" }} Assistant: +{{- end }} {{ .Content }}<|end_of_turn|> +{{- end }}GPT4 Correct Assistant: \ No newline at end of file diff --git a/template/phi-3.gotmpl b/template/phi-3.gotmpl index 6c3610dd..abec2137 100644 --- a/template/phi-3.gotmpl +++ b/template/phi-3.gotmpl @@ -1,6 +1,3 @@ -{{ if .System }}<|system|> -{{ .System }}<|end|> -{{ end }}{{ if .Prompt }}<|user|> -{{ .Prompt }}<|end|> +{{- range .Messages }}<|{{ .Role }}|> +{{ .Content }}<|end|> {{ end }}<|assistant|> -{{ .Response }}<|end|> diff --git a/template/solar-instruct.gotmpl b/template/solar-instruct.gotmpl index 1c14960d..263bde80 100644 --- a/template/solar-instruct.gotmpl +++ b/template/solar-instruct.gotmpl @@ -1,9 +1,11 @@ -{{ if .System }}### System: -{{ .System }} +{{- range .Messages }} +{{- if eq .Role "system" }}### System: +{{- else if eq .Role "user" }}### User: +{{- else if eq .Role "assistant" }}### Assistant: +{{ .Content }} -{{ end }}{{ if .Prompt }}### User: -{{ .Prompt }} +{{ continue }} +{{- end }} +{{ .Content }} {{ end }}### Assistant: -{{ .Response }} - diff --git a/template/starcoder2-instruct.gotmpl b/template/starcoder2-instruct.gotmpl index 6c93a7ab..7963b4f9 100644 --- a/template/starcoder2-instruct.gotmpl +++ b/template/starcoder2-instruct.gotmpl @@ -1,8 +1,18 @@ -{{ if .System }}{{ .System }} +{{- $system := "" }} +{{- range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- else if eq .Role "user" }} +{{- if $system }}{{ $system }} -{{ end }}{{ if .Prompt }}### Instruction -{{ .Prompt }} +{{ $system = "" }} +{{- end }}### Instruction +{{ .Content }} -{{ end }}### Response -{{ .Response }}<|endoftext|> +{{ else if eq .Role "assistant" }}### Response +{{ .Content }}<|endoftext|> +{{ end }} +{{- end }}### Response diff --git a/template/vicuna.gotmpl b/template/vicuna.gotmpl index 515b2fe9..c27f39c5 100644 --- a/template/vicuna.gotmpl +++ b/template/vicuna.gotmpl @@ -1,4 +1,14 @@ -{{ if .System }}{{ .System }} +{{- $system := "" }} +{{- range .Messages }} +{{- if eq .Role "system" }} +{{- if not $system }}{{ $system = .Content }} +{{- else }}{{ $system = printf "%s\n\n%s" $system .Content }} +{{- end }} +{{- else if eq .Role "user" }} +{{- if $system }}{{ $system }} -{{ end }}{{ if .Prompt }}USER: {{ .Prompt }} -{{ end }}ASSISTANT: {{ .Response }} +{{ $system = "" }} +{{- end }}USER: {{ .Content }} +{{ else if eq .Role "assistant" }}ASSISTANT: {{ .Content }} +{{ end }} +{{- end }}ASSISTANT: \ No newline at end of file diff --git a/template/zephyr.gotmpl b/template/zephyr.gotmpl index 1f889f26..25da148a 100644 --- a/template/zephyr.gotmpl +++ b/template/zephyr.gotmpl @@ -1,6 +1,3 @@ -{{ if .System }}<|system|> -{{ .System }} -{{ end }}{{ if .Prompt }}<|user|> -{{ .Prompt }} +{{- range .Messages }}<|{{ .Role }}|> +{{ .Content }} {{ end }}<|assistant|> -{{ .Response }} From 93ea9240aee8a51b0fe455fdddedec2046438f95 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 27 Aug 2024 16:19:00 -0700 Subject: [PATCH 083/174] Move ollama executable out of bin dir (#6535) --- app/ollama.iss | 8 ++++---- envconfig/config.go | 11 ++++++++++- gpu/amd_common.go | 4 +++- gpu/amd_windows.go | 2 +- gpu/gpu.go | 2 +- scripts/build_windows.ps1 | 4 ++-- 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app/ollama.iss b/app/ollama.iss index bce0a337..34cc5c4c 100644 --- a/app/ollama.iss +++ b/app/ollama.iss @@ -87,7 +87,7 @@ DialogFontSize=12 [Files] Source: ".\app.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}" ; Flags: ignoreversion 64bit -Source: "..\ollama.exe"; DestDir: "{app}\bin"; Flags: ignoreversion 64bit +Source: "..\ollama.exe"; DestDir: "{app}"; Flags: ignoreversion 64bit Source: "..\dist\windows-{#ARCH}\lib\ollama\runners\*"; DestDir: "{app}\lib\ollama\runners"; Flags: ignoreversion 64bit recursesubdirs Source: "..\dist\ollama_welcome.ps1"; DestDir: "{app}"; Flags: ignoreversion Source: ".\assets\app.ico"; DestDir: "{app}"; Flags: ignoreversion @@ -99,7 +99,7 @@ Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilen Name: "{userprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\app.ico" [Run] -Filename: "{cmd}"; Parameters: "/C set PATH={app}\bin;%PATH% & ""{app}\{#MyAppExeName}"""; Flags: postinstall nowait runhidden +Filename: "{cmd}"; Parameters: "/C set PATH={app};%PATH% & ""{app}\{#MyAppExeName}"""; Flags: postinstall nowait runhidden [UninstallRun] ; Filename: "{cmd}"; Parameters: "/C ""taskkill /im ''{#MyAppExeName}'' /f /t"; Flags: runhidden @@ -134,8 +134,8 @@ SetupAppRunningError=Another Ollama installer is running.%n%nPlease cancel or fi [Registry] Root: HKCU; Subkey: "Environment"; \ - ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; \ - Check: NeedsAddPath('{app}\bin') + ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \ + Check: NeedsAddPath('{app}') [Code] diff --git a/envconfig/config.go b/envconfig/config.go index 7e45a4f5..806a2d08 100644 --- a/envconfig/config.go +++ b/envconfig/config.go @@ -190,7 +190,7 @@ func RunnersDir() (p string) { } var paths []string - for _, root := range []string{filepath.Dir(exe), filepath.Join(filepath.Dir(exe), ".."), cwd} { + for _, root := range []string{filepath.Dir(exe), filepath.Join(filepath.Dir(exe), LibRelativeToExe()), cwd} { paths = append(paths, root, filepath.Join(root, runtime.GOOS+"-"+runtime.GOARCH), @@ -282,3 +282,12 @@ func Values() map[string]string { func Var(key string) string { return strings.Trim(strings.TrimSpace(os.Getenv(key)), "\"'") } + +// On windows, we keep the binary at the top directory, but +// other platforms use a "bin" directory, so this returns ".." +func LibRelativeToExe() string { + if runtime.GOOS == "windows" { + return "." + } + return ".." +} diff --git a/gpu/amd_common.go b/gpu/amd_common.go index 72d204f7..2894ac2c 100644 --- a/gpu/amd_common.go +++ b/gpu/amd_common.go @@ -9,6 +9,8 @@ import ( "path/filepath" "runtime" "strings" + + "github.com/ollama/ollama/envconfig" ) // Determine if the given ROCm lib directory is usable by checking for existence of some glob patterns @@ -54,7 +56,7 @@ func commonAMDValidateLibDir() (string, error) { // Installer payload location if we're running the installed binary exe, err := os.Executable() if err == nil { - rocmTargetDir := filepath.Join(filepath.Dir(exe), "..", "lib", "ollama") + rocmTargetDir := filepath.Join(filepath.Dir(exe), envconfig.LibRelativeToExe(), "lib", "ollama") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ROCM next to ollama executable " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/amd_windows.go b/gpu/amd_windows.go index a0ae7c96..ef6bf830 100644 --- a/gpu/amd_windows.go +++ b/gpu/amd_windows.go @@ -153,7 +153,7 @@ func AMDValidateLibDir() (string, error) { // Installer payload (if we're running from some other location) localAppData := os.Getenv("LOCALAPPDATA") appDir := filepath.Join(localAppData, "Programs", "Ollama") - rocmTargetDir := filepath.Join(appDir, "..", "lib", "ollama") + rocmTargetDir := filepath.Join(appDir, envconfig.LibRelativeToExe(), "lib", "ollama") if rocmLibUsable(rocmTargetDir) { slog.Debug("detected ollama installed ROCm at " + rocmTargetDir) return rocmTargetDir, nil diff --git a/gpu/gpu.go b/gpu/gpu.go index 10afb1e3..3de93f7f 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -653,7 +653,7 @@ func LibraryDir() string { slog.Warn("failed to lookup working directory", "error", err) } // Scan for any of our dependeices, and pick first match - for _, root := range []string{filepath.Dir(appExe), filepath.Join(filepath.Dir(appExe), ".."), cwd} { + for _, root := range []string{filepath.Dir(appExe), filepath.Join(filepath.Dir(appExe), envconfig.LibRelativeToExe()), cwd} { libDep := filepath.Join("lib", "ollama") if _, err := os.Stat(filepath.Join(root, libDep)); err == nil { return filepath.Join(root, libDep) diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1 index 9cebf1f4..eb8570c8 100644 --- a/scripts/build_windows.ps1 +++ b/scripts/build_windows.ps1 @@ -122,8 +122,8 @@ function buildOllama() { /csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} ollama.exe if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)} } - New-Item -ItemType Directory -Path .\dist\windows-${script:TARGET_ARCH}\bin\ -Force - cp .\ollama.exe .\dist\windows-${script:TARGET_ARCH}\bin\ + New-Item -ItemType Directory -Path .\dist\windows-${script:TARGET_ARCH}\ -Force + cp .\ollama.exe .\dist\windows-${script:TARGET_ARCH}\ } function buildApp() { From 6c1c1ad6a90e8fe23d63d2c431745e48e3fe9d81 Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Tue, 27 Aug 2024 17:54:04 -0700 Subject: [PATCH 084/174] throw an error when encountering unsupport tensor sizes (#6538) --- convert/convert_test.go | 101 ++++++++++++++++++++++++++++++++++ convert/reader_safetensors.go | 5 ++ 2 files changed, 106 insertions(+) diff --git a/convert/convert_test.go b/convert/convert_test.go index 56b34f22..f71ff8cd 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -140,6 +140,107 @@ func TestConvertFull(t *testing.T) { } } +func TestConvertInvalidDatatype(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "testmodel") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + tempDir := t.TempDir() + generateSafetensorTestData(t, tempDir) + + err = ConvertModel(os.DirFS(tempDir), f) + if err == nil || err.Error() != "unsupported safetensors model" { + t.Errorf("expected error but didn't get one") + } +} + +func generateSafetensorTestData(t *testing.T, tempDir string) { + type tensorData struct { + Offsets []int `json:"data_offsets"` + Type string `json:"dtype"` + Shape []int `json:"shape"` + } + offset := 4096 * 14336 + + td := map[string]*tensorData{} + td["model.layers.0.mlp.down_proj.weight"] = &tensorData{ + Offsets: []int{0, offset}, + Type: "I8", + Shape: []int{4096, 14336}, + } + td["model.layers.0.mlp.down_proj.weight_format"] = &tensorData{ + Offsets: []int{offset, offset}, + Type: "U8", + Shape: []int{}, + } + + data, err := json.Marshal(td) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + l := int64(len(data)) + err = binary.Write(&buf, binary.LittleEndian, l) + if err != nil { + t.Fatal(err) + } + + _, err = buf.Write(data) + if err != nil { + t.Fatal(err) + } + + fdata, err := os.Create(filepath.Join(tempDir, "model-00001-of-00001.safetensors")) + if err != nil { + t.Fatal(err) + } + defer fdata.Close() + + _, err = fdata.Write(buf.Bytes()) + if err != nil { + t.Fatal(err) + } + + configData := ` +{ + "architectures": [ + "LlamaForCausalLM" + ] +} +` + + f, err := os.Create(filepath.Join(tempDir, "config.json")) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + _, err = f.WriteString(configData) + if err != nil { + t.Fatal(err) + } + + tokenizerData := ` +{ +} +` + + f, err = os.Create(filepath.Join(tempDir, "tokenizer.json")) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + _, err = f.WriteString(tokenizerData) + if err != nil { + t.Fatal(err) + } +} + func TestConvertAdapter(t *testing.T) { type AdapterCase struct { Name string diff --git a/convert/reader_safetensors.go b/convert/reader_safetensors.go index 32a362cd..e1dde8fa 100644 --- a/convert/reader_safetensors.go +++ b/convert/reader_safetensors.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "io" "io/fs" @@ -50,6 +51,10 @@ func parseSafetensors(fsys fs.FS, replacer *strings.Replacer, ps ...string) ([]T for _, key := range keys { if value := headers[key]; value.Type != "" { + // bitsandbytes quantized models are unsupported + if len(value.Shape) == 0 { + return nil, errors.New("unsupported safetensors model") + } ts = append(ts, safetensor{ fs: fsys, path: p, From d9d50c43cca950e70cdd288d62c58a8d5a8e43d4 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 27 Aug 2024 17:56:04 -0700 Subject: [PATCH 085/174] validate model path --- server/modelpath.go | 18 +++++------------- server/modelpath_test.go | 8 ++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/server/modelpath.go b/server/modelpath.go index 354eeed7..d498c467 100644 --- a/server/modelpath.go +++ b/server/modelpath.go @@ -73,18 +73,6 @@ func ParseModelPath(name string) ModelPath { var errModelPathInvalid = errors.New("invalid model path") -func (mp ModelPath) Validate() error { - if mp.Repository == "" { - return fmt.Errorf("%w: model repository name is required", errModelPathInvalid) - } - - if strings.Contains(mp.Tag, ":") { - return fmt.Errorf("%w: ':' (colon) is not allowed in tag names", errModelPathInvalid) - } - - return nil -} - func (mp ModelPath) GetNamespaceRepository() string { return fmt.Sprintf("%s/%s", mp.Namespace, mp.Repository) } @@ -105,7 +93,11 @@ func (mp ModelPath) GetShortTagname() string { // GetManifestPath returns the path to the manifest file for the given model path, it is up to the caller to create the directory if it does not exist. func (mp ModelPath) GetManifestPath() (string, error) { - return filepath.Join(envconfig.Models(), "manifests", mp.Registry, mp.Namespace, mp.Repository, mp.Tag), nil + if p := filepath.Join(mp.Registry, mp.Namespace, mp.Repository, mp.Tag); filepath.IsLocal(p) { + return filepath.Join(envconfig.Models(), "manifests", p), nil + } + + return "", errModelPathInvalid } func (mp ModelPath) BaseURL() *url.URL { diff --git a/server/modelpath_test.go b/server/modelpath_test.go index 849e0fa7..ef26266b 100644 --- a/server/modelpath_test.go +++ b/server/modelpath_test.go @@ -1,6 +1,7 @@ package server import ( + "errors" "os" "path/filepath" "testing" @@ -154,3 +155,10 @@ func TestParseModelPath(t *testing.T) { }) } } + +func TestInsecureModelpath(t *testing.T) { + mp := ParseModelPath("../../..:something") + if _, err := mp.GetManifestPath(); !errors.Is(err, errModelPathInvalid) { + t.Errorf("expected error: %v", err) + } +} From 8e6da3cbc57de9dce71bb6f0b13ab2929af1474f Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Tue, 27 Aug 2024 17:57:34 -0700 Subject: [PATCH 086/174] update deprecated warnings --- .golangci.yaml | 4 ++++ api/types.go | 16 +++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index c9c9f620..2e0ed3c7 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -32,6 +32,10 @@ linters: linters-settings: gci: sections: [standard, default, localmodule] + staticcheck: + checks: + - all + - -SA1019 # omit Deprecated check severity: default-severity: error rules: diff --git a/api/types.go b/api/types.go index 2f5a9424..df7bab21 100644 --- a/api/types.go +++ b/api/types.go @@ -296,15 +296,17 @@ type EmbeddingResponse struct { // CreateRequest is the request passed to [Client.Create]. type CreateRequest struct { Model string `json:"model"` - Path string `json:"path"` Modelfile string `json:"modelfile"` Stream *bool `json:"stream,omitempty"` Quantize string `json:"quantize,omitempty"` - // Name is deprecated, see Model + // Deprecated: set the model name with Model instead Name string `json:"name"` - // Quantization is deprecated, see Quantize + // Deprecated: set the file content with Modelfile instead + Path string `json:"path"` + + // Deprecated: use Quantize instead Quantization string `json:"quantization,omitempty"` } @@ -312,7 +314,7 @@ type CreateRequest struct { type DeleteRequest struct { Model string `json:"model"` - // Name is deprecated, see Model + // Deprecated: set the model name with Model instead Name string `json:"name"` } @@ -327,7 +329,7 @@ type ShowRequest struct { Options map[string]interface{} `json:"options"` - // Name is deprecated, see Model + // Deprecated: set the model name with Model instead Name string `json:"name"` } @@ -359,7 +361,7 @@ type PullRequest struct { Password string `json:"password"` Stream *bool `json:"stream,omitempty"` - // Name is deprecated, see Model + // Deprecated: set the model name with Model instead Name string `json:"name"` } @@ -380,7 +382,7 @@ type PushRequest struct { Password string `json:"password"` Stream *bool `json:"stream,omitempty"` - // Name is deprecated, see Model + // Deprecated: set the model name with Model instead Name string `json:"name"` } From 7416ced70f02f18f353d4025b817148d04867a3f Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Wed, 28 Aug 2024 14:03:20 -0700 Subject: [PATCH 087/174] add llama3.1 chat template (#6545) --- template/index.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/template/index.json b/template/index.json index e2d41893..0ce6ac0f 100644 --- a/template/index.json +++ b/template/index.json @@ -91,6 +91,10 @@ "template": "{% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{% if add_generation_prompt %}{{ '<|start_header_id|>assistant<|end_header_id|>\n\n' }}{% endif %}", "name": "llama3-instruct" }, + { + "template": "{{- bos_token }}\n{%- if custom_tools is defined %}\n {%- set tools = custom_tools %}\n{%- endif %}\n{%- if not tools_in_user_message is defined %}\n {%- set tools_in_user_message = true %}\n{%- endif %}\n{%- if not date_string is defined %}\n {%- set date_string = \"26 Jul 2024\" %}\n{%- endif %}\n{%- if not tools is defined %}\n {%- set tools = none %}\n{%- endif %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content']|trim %}\n {%- set messages = messages[1:] %}\n{%- else %}\n {%- set system_message = \"\" %}\n{%- endif %}\n\n{#- System message + builtin tools #}\n{{- \"<|start_header_id|>system<|end_header_id|>\\n\\n\" }}\n{%- if builtin_tools is defined or tools is not none %}\n {{- \"Environment: ipython\\n\" }}\n{%- endif %}\n{%- if builtin_tools is defined %}\n {{- \"Tools: \" + builtin_tools | reject('equalto', 'code_interpreter') | join(\", \") + \"\\n\\n\"}}\n{%- endif %}\n{{- \"Cutting Knowledge Date: December 2023\\n\" }}\n{{- \"Today Date: \" + date_string + \"\\n\\n\" }}\n{%- if tools is not none and not tools_in_user_message %}\n {{- \"You have access to the following functions. To call a function, please respond with JSON for a function call.\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\\n\\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\\n\\n\" }}\n {%- endfor %}\n{%- endif %}\n{{- system_message }}\n{{- \"<|eot_id|>\" }}\n\n{#- Custom tools are passed in a user message with some extra guidance #}\n{%- if tools_in_user_message and not tools is none %}\n {#- Extract the first user message so we can plug it in here #}\n {%- if messages | length != 0 %}\n {%- set first_user_message = messages[0]['content']|trim %}\n {%- set messages = messages[1:] %}\n {%- else %}\n {{- raise_exception(\"Cannot put tools in the first user message when there's no first user message!\") }}\n{%- endif %}\n {{- '<|start_header_id|>user<|end_header_id|>\\n\\n' -}}\n {{- \"Given the following functions, please respond with a JSON for a function call \" }}\n {{- \"with its proper arguments that best answers the given prompt.\\n\\n\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\\n\\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\\n\\n\" }}\n {%- endfor %}\n {{- first_user_message + \"<|eot_id|>\"}}\n{%- endif %}\n\n{%- for message in messages %}\n {%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n'+ message['content'] | trim + '<|eot_id|>' }}\n {%- elif 'tool_calls' in message %}\n {%- if not message.tool_calls|length == 1 %}\n {{- raise_exception(\"This model only supports single tool-calls at once!\") }}\n {%- endif %}\n {%- set tool_call = message.tool_calls[0].function %}\n {%- if builtin_tools is defined and tool_call.name in builtin_tools %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' -}}\n {{- \"<|python_tag|>\" + tool_call.name + \".call(\" }}\n {%- for arg_name, arg_val in tool_call.arguments | items %}\n {{- arg_name + '=\"' + arg_val + '\"' }}\n {%- if not loop.last %}\n {{- \", \" }}\n {%- endif %}\n {%- endfor %}\n {{- \")\" }}\n {%- else %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' -}}\n {{- '{\"name\": \"' + tool_call.name + '\", ' }}\n {{- '\"parameters\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- \"}\" }}\n {%- endif %}\n {%- if builtin_tools is defined %}\n {#- This means we're in ipython mode #}\n {{- \"<|eom_id|>\" }}\n {%- else %}\n {{- \"<|eot_id|>\" }}\n {%- endif %}\n {%- elif message.role == \"tool\" or message.role == \"ipython\" %}\n {{- \"<|start_header_id|>ipython<|end_header_id|>\\n\\n\" }}\n {%- if message.content is mapping or message.content is iterable %}\n {{- message.content | tojson }}\n {%- else %}\n {{- message.content }}\n {%- endif %}\n {{- \"<|eot_id|>\" }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}\n", + "name": "llama3-instruct" + }, { "template": "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ 'Question:\n' + message['content'] + '\n\n' }}{% elif message['role'] == 'system' %}\n{{ 'System:\n' + message['content'] + '\n\n' }}{% elif message['role'] == 'assistant' %}{{ 'Answer:\n' + message['content'] + '\n\n' }}{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ 'Answer:\n' }}{% endif %}{% endfor %}", "name": "granite-instruct" From e4d0a9c325137b684d9c0bb02694e8f10197314a Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Wed, 28 Aug 2024 14:07:48 -0700 Subject: [PATCH 088/174] fix(test): do not clobber models directory --- server/model_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/model_test.go b/server/model_test.go index 7753c549..e1737a5b 100644 --- a/server/model_test.go +++ b/server/model_test.go @@ -139,6 +139,7 @@ The temperature in San Francisco, CA is 70°F and in Toronto, Canada is 20°C.`, func TestParseFromFileFromLayer(t *testing.T) { tempModels := t.TempDir() + t.Setenv("OLLAMA_MODELS", tempModels) file, err := os.CreateTemp(tempModels, "") if err != nil { @@ -189,6 +190,7 @@ func TestParseFromFileFromLayer(t *testing.T) { func TestParseLayerFromCopy(t *testing.T) { tempModels := t.TempDir() + t.Setenv("OLLAMA_MODELS", tempModels) file2, err := os.CreateTemp(tempModels, "") if err != nil { From 8e4e509fa4e8e1c49cedfc2754e9a0c9ed0f2fae Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Wed, 28 Aug 2024 17:11:46 -0700 Subject: [PATCH 089/174] update the openai docs to explain how to set the context size (#6548) --- docs/openai.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/openai.md b/docs/openai.md index 75d2c595..0cbea6cc 100644 --- a/docs/openai.md +++ b/docs/openai.md @@ -300,3 +300,28 @@ curl http://localhost:11434/v1/chat/completions \ ] }' ``` + +### Setting the context size + +The OpenAI API does not have a way of setting the context size for a model. If you need to change the context size, create a `Modelfile` which looks like: + +```modelfile +FROM +PARAMETER num_ctx +``` + +Use the `ollama create mymodel` command to create a new model with the updated context size. Call the API with the updated model name: + +```shell +curl http://localhost:11434/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "mymodel", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }' +``` From 56346ccfa3e51eec51fc26ae8e91fc88cb74a9b8 Mon Sep 17 00:00:00 2001 From: Bryan Honof Date: Thu, 29 Aug 2024 18:45:35 +0200 Subject: [PATCH 090/174] doc: Add Nix and Flox to package manager listing (#6074) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index aae92e6c..9f643800 100644 --- a/README.md +++ b/README.md @@ -337,6 +337,8 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Pacman](https://archlinux.org/packages/extra/x86_64/ollama/) - [Helm Chart](https://artifacthub.io/packages/helm/ollama-helm/ollama) - [Guix channel](https://codeberg.org/tusharhero/ollama-guix) +- [Nix package](https://search.nixos.org/packages?channel=24.05&show=ollama&from=0&size=50&sort=relevance&type=packages&query=ollama) +- [Flox](https://flox.dev/blog/ollama-part-one) ### Libraries From 11018196e0e15b78328f710cef707c09eabcbd8d Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 29 Aug 2024 13:40:43 -0700 Subject: [PATCH 091/174] remove any unneeded build artifacts --- llm/generate/gen_common.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llm/generate/gen_common.sh b/llm/generate/gen_common.sh index 40115936..cef68ea1 100644 --- a/llm/generate/gen_common.sh +++ b/llm/generate/gen_common.sh @@ -87,6 +87,8 @@ apply_patches() { build() { cmake -S ${LLAMACPP_DIR} -B ${BUILD_DIR} ${CMAKE_DEFS} cmake --build ${BUILD_DIR} ${CMAKE_TARGETS} -j8 + # remove unnecessary build artifacts + rm -f ${BUILD_DIR}/bin/ggml-common.h ${BUILD_DIR}/bin/ggml-metal.metal } compress() { From a1cef4d0a5f31280ea82b350605775931a6163cb Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Sat, 31 Aug 2024 10:40:05 -0700 Subject: [PATCH 092/174] Add findutils to base images (#6581) This caused missing internal files --- scripts/rh_linux_deps.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/rh_linux_deps.sh b/scripts/rh_linux_deps.sh index b4c9afd6..23f1f650 100644 --- a/scripts/rh_linux_deps.sh +++ b/scripts/rh_linux_deps.sh @@ -30,7 +30,7 @@ if grep -i "centos" /etc/system-release >/dev/null; then dnf install -y rh-git227-git ln -s /opt/rh/rh-git227/root/usr/bin/git /usr/local/bin/git fi - dnf install -y devtoolset-10-gcc devtoolset-10-gcc-c++ pigz + dnf install -y devtoolset-10-gcc devtoolset-10-gcc-c++ pigz findutils elif grep -i "rocky" /etc/system-release >/dev/null; then # Temporary workaround until rocky 8 AppStream ships GCC 10.4 (10.3 is incompatible with NVCC) cat << EOF > /etc/yum.repos.d/Rocky-Vault.repo @@ -45,6 +45,7 @@ EOF dnf install -y git \ gcc-toolset-10-gcc-10.2.1-8.2.el8 \ gcc-toolset-10-gcc-c++-10.2.1-8.2.el8 \ + findutils \ pigz else echo "ERROR Unexpected distro" From 1aad838707227eaf1be92ff6dd2fd3a6662858c2 Mon Sep 17 00:00:00 2001 From: rayfiyo <108730891+rayfiyo@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:34:25 +0900 Subject: [PATCH 093/174] docs: update GGUF examples and references (#6577) --- docs/modelfile.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modelfile.md b/docs/modelfile.md index 51827e74..92df22ef 100644 --- a/docs/modelfile.md +++ b/docs/modelfile.md @@ -128,10 +128,10 @@ Currently supported model architectures: #### Build from a GGUF file ```modelfile -FROM ./ollama-model.bin +FROM ./ollama-model.gguf ``` -The GGUF bin file location should be specified as an absolute path or relative to the `Modelfile` location. +The GGUF file location should be specified as an absolute path or relative to the `Modelfile` location. ### PARAMETER @@ -208,7 +208,7 @@ Currently supported Safetensor adapters: #### GGUF adapter ```modelfile -ADAPTER ./ollama-lora.bin +ADAPTER ./ollama-lora.gguf ``` ### LICENSE From 5f7b4a5e3056d083997b744029c30614cd32397b Mon Sep 17 00:00:00 2001 From: Vimal Kumar Date: Sun, 1 Sep 2024 09:42:17 +0530 Subject: [PATCH 094/174] fix(cmd): show info may have nil ModelInfo (#6579) --- cmd/cmd.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index b75c0b5e..f6d31f5b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -726,14 +726,17 @@ func ShowHandler(cmd *cobra.Command, args []string) error { } func showInfo(resp *api.ShowResponse) { - arch := resp.ModelInfo["general.architecture"].(string) - modelData := [][]string{ - {"arch", arch}, {"parameters", resp.Details.ParameterSize}, {"quantization", resp.Details.QuantizationLevel}, - {"context length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.context_length", arch)].(float64))}, - {"embedding length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.embedding_length", arch)].(float64))}, + } + if resp.ModelInfo != nil { + arch := resp.ModelInfo["general.architecture"].(string) + modelData = append(modelData, + []string{"arch", arch}, + []string{"context length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.context_length", arch)].(float64))}, + []string{"embedding length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.embedding_length", arch)].(float64))}, + ) } mainTableData := [][]string{ From 741affdfd6938bebcec34a2ee4c40fe2584a63bc Mon Sep 17 00:00:00 2001 From: SnoopyTlion <771933002@qq.com> Date: Tue, 3 Sep 2024 03:31:29 +0800 Subject: [PATCH 095/174] docs: update faq.md for OLLAMA_MODELS env var permissions (#6587) --- docs/faq.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 25b68248..356d5105 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -194,6 +194,8 @@ Refer to the section [above](#how-do-i-configure-ollama-server) for how to set e If a different directory needs to be used, set the environment variable `OLLAMA_MODELS` to the chosen directory. +> Note: on Linux using the standard installer, the `ollama` user needs read and write access to the specified directory. To assign the directory to the `ollama` user run `sudo chown -R ollama:ollama `. + Refer to the section [above](#how-do-i-configure-ollama-server) for how to set environment variables on your platform. ## How can I use Ollama in Visual Studio Code? From bfc2d61549534687277256590dce16864dda40c7 Mon Sep 17 00:00:00 2001 From: Jonathan Hecl Date: Mon, 2 Sep 2024 16:34:26 -0300 Subject: [PATCH 096/174] readme: add go-crew and Ollamaclient projects (#6583) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9f643800..1767d2dc 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [LLMStack](https://github.com/trypromptly/LLMStack) (No-code multi-agent framework to build LLM agents and workflows) - [BoltAI for Mac](https://boltai.com) (AI Chat Client for Mac) - [Harbor](https://github.com/av/harbor) (Containerized LLM Toolkit with Ollama as default backend) +- [Go-CREW](https://www.jonathanhecl.com/go-crew/) (Powerful Offline RAG in Golang) ### Terminal @@ -370,6 +371,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Portkey](https://portkey.ai/docs/welcome/integration-guides/ollama) - [PromptingTools.jl](https://github.com/svilupp/PromptingTools.jl) with an [example](https://svilupp.github.io/PromptingTools.jl/dev/examples/working_with_ollama) - [LlamaScript](https://github.com/Project-Llama/llamascript) +- [Ollamaclient for Golang](https://github.com/xyproto/ollamaclient) ### Mobile From ad3eb00bee71052576807b8cf4b9a25b2cceba50 Mon Sep 17 00:00:00 2001 From: presbrey Date: Mon, 2 Sep 2024 16:05:36 -0400 Subject: [PATCH 097/174] readme: add OllamaFarm project (#6508) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1767d2dc..84368f8d 100644 --- a/README.md +++ b/README.md @@ -350,6 +350,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [LangChainRust](https://github.com/Abraxas-365/langchain-rust) with [example](https://github.com/Abraxas-365/langchain-rust/blob/main/examples/llm_ollama.rs) - [LlamaIndex](https://gpt-index.readthedocs.io/en/stable/examples/llm/ollama.html) - [LiteLLM](https://github.com/BerriAI/litellm) +- [OllamaFarm for Go](https://github.com/presbrey/ollamafarm) - [OllamaSharp for .NET](https://github.com/awaescher/OllamaSharp) - [Ollama for Ruby](https://github.com/gbaptista/ollama-ai) - [Ollama-rs for Rust](https://github.com/pepperoni21/ollama-rs) From 9df5f0e8e4d9a7f2f0fdfc782304df8dbcbd50c6 Mon Sep 17 00:00:00 2001 From: R0CKSTAR Date: Wed, 4 Sep 2024 00:25:31 +0800 Subject: [PATCH 098/174] Reduce docker image size (#5847) Signed-off-by: Xiaodong Ye --- Dockerfile | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index c46477b4..6743866a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG CUDA_V11_ARCHITECTURES -ENV GOARCH amd64 +ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ @@ -38,7 +38,7 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG CUDA_V12_ARCHITECTURES -ENV GOARCH amd64 +ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ @@ -56,7 +56,7 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG CUDA_V11_ARCHITECTURES -ENV GOARCH arm64 +ENV GOARCH arm64 RUN OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ CMAKE_CUDA_ARCHITECTURES="${CUDA_V11_ARCHITECTURES}" \ @@ -72,7 +72,7 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG CUDA_V12_ARCHITECTURES -ENV GOARCH arm64 +ENV GOARCH arm64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 \ OLLAMA_SKIP_CPU_GENERATE=1 \ @@ -92,7 +92,7 @@ COPY --from=llm-code / /go/src/github.com/ollama/ollama/ WORKDIR /go/src/github.com/ollama/ollama/llm/generate ARG CGO_CFLAGS ARG AMDGPU_TARGETS -ENV GOARCH amd64 +ENV GOARCH amd64 RUN --mount=type=cache,target=/root/.ccache \ OLLAMA_SKIP_STATIC_GENERATE=1 OLLAMA_SKIP_CPU_GENERATE=1 bash gen_linux.sh RUN mkdir -p ../../dist/linux-amd64-rocm/lib/ollama && \ @@ -107,7 +107,7 @@ ENV PATH /opt/rh/devtoolset-10/root/usr/bin:$PATH COPY --from=llm-code / /go/src/github.com/ollama/ollama/ ARG OLLAMA_CUSTOM_CPU_DEFS ARG CGO_CFLAGS -ENV GOARCH amd64 +ENV GOARCH amd64 WORKDIR /go/src/github.com/ollama/ollama/llm/generate FROM --platform=linux/amd64 cpu-builder-amd64 AS static-build-amd64 @@ -181,17 +181,19 @@ RUN --mount=type=cache,target=/root/.ccache \ # Strip out ROCm dependencies to keep the primary image lean FROM --platform=linux/amd64 ubuntu:22.04 as amd64-libs-without-rocm COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/lib/ /scratch/ -RUN cd /scratch/ollama/ && rm -rf rocblas libamd* libdrm* libroc* libhip* libhsa* +RUN cd /scratch/ollama/ && rm -rf rocblas libamd* libdrm* libroc* libhip* libhsa* # Runtime stages FROM --platform=linux/amd64 ubuntu:22.04 as runtime-amd64 COPY --from=amd64-libs-without-rocm /scratch/ /lib/ -RUN apt-get update && apt-get install -y ca-certificates +RUN apt-get update && apt-get install -y ca-certificates && \ + apt-get clean && rm -rf /var/lib/apt/lists/* COPY --from=build-amd64 /go/src/github.com/ollama/ollama/dist/linux-amd64/bin/ /bin/ FROM --platform=linux/arm64 ubuntu:22.04 as runtime-arm64 COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/lib/ /lib/ -RUN apt-get update && apt-get install -y ca-certificates +RUN apt-get update && apt-get install -y ca-certificates && \ + apt-get clean && rm -rf /var/lib/apt/lists/* COPY --from=build-arm64 /go/src/github.com/ollama/ollama/dist/linux-arm64/bin/ /bin/ # Radeon images are much larger so we keep it distinct from the CPU/CUDA image From 14d5093cd02373f11ed138241e3e2eedf349824b Mon Sep 17 00:00:00 2001 From: OpenVMP Date: Tue, 3 Sep 2024 09:28:01 -0700 Subject: [PATCH 099/174] readme: add PartCAD tool to readme for generating 3D CAD models using Ollama (#6605) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84368f8d..3b619ae0 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [BoltAI for Mac](https://boltai.com) (AI Chat Client for Mac) - [Harbor](https://github.com/av/harbor) (Containerized LLM Toolkit with Ollama as default backend) - [Go-CREW](https://www.jonathanhecl.com/go-crew/) (Powerful Offline RAG in Golang) +- [PartCAD](https://github.com/openvmp/partcad/) (CAD model generation with OpenSCAD and CadQuery) ### Terminal From 94fff5805fecd25287e1a21ac9910859011adcff Mon Sep 17 00:00:00 2001 From: FellowTraveler Date: Tue, 3 Sep 2024 11:32:59 -0500 Subject: [PATCH 100/174] Fix sprintf to snprintf (#5664) /Users/au/src/ollama/llm/ext_server/server.cpp:289:9: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only. Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. --- llm/ext_server/server.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llm/ext_server/server.cpp b/llm/ext_server/server.cpp index 8e08b850..1e230611 100644 --- a/llm/ext_server/server.cpp +++ b/llm/ext_server/server.cpp @@ -262,7 +262,7 @@ struct server_slot { char buffer[512]; double t_token = t_prompt_processing / n_prompt_tokens_processed; double n_tokens_second = 1e3 / t_prompt_processing * n_prompt_tokens_processed; - sprintf(buffer, "prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)", + snprintf(buffer, sizeof(buffer), "prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)", t_prompt_processing, n_prompt_tokens_processed, t_token, n_tokens_second); LOG_DEBUG(buffer, { @@ -276,7 +276,7 @@ struct server_slot { t_token = t_token_generation / n_decoded; n_tokens_second = 1e3 / t_token_generation * n_decoded; - sprintf(buffer, "generation eval time = %10.2f ms / %5d runs (%8.2f ms per token, %8.2f tokens per second)", + snprintf(buffer, sizeof(buffer), "generation eval time = %10.2f ms / %5d runs (%8.2f ms per token, %8.2f tokens per second)", t_token_generation, n_decoded, t_token, n_tokens_second); LOG_DEBUG(buffer, { @@ -288,7 +288,7 @@ struct server_slot { {"n_tokens_second", n_tokens_second}, }); - sprintf(buffer, " total time = %10.2f ms", t_prompt_processing + t_token_generation); + snprintf(buffer, sizeof(buffer), " total time = %10.2f ms", t_prompt_processing + t_token_generation); LOG_DEBUG(buffer, { {"slot_id", id}, {"task_id", task_id}, From 35159de18a6054f30d6eb73747f150c519cbf0b9 Mon Sep 17 00:00:00 2001 From: Amith Koujalgi Date: Wed, 4 Sep 2024 01:38:50 +0530 Subject: [PATCH 101/174] readme: update Ollama4j link and add link to Ollama4j Web UI (#6608) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b619ae0..5568b6e7 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Harbor](https://github.com/av/harbor) (Containerized LLM Toolkit with Ollama as default backend) - [Go-CREW](https://www.jonathanhecl.com/go-crew/) (Powerful Offline RAG in Golang) - [PartCAD](https://github.com/openvmp/partcad/) (CAD model generation with OpenSCAD and CadQuery) +- [Ollama4j Web UI](https://github.com/ollama4j/ollama4j-web-ui) - Java-based Web UI for Ollama built with Vaadin, Spring Boot and Ollama4j ### Terminal @@ -356,7 +357,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Ollama for Ruby](https://github.com/gbaptista/ollama-ai) - [Ollama-rs for Rust](https://github.com/pepperoni21/ollama-rs) - [Ollama-hpp for C++](https://github.com/jmont-dev/ollama-hpp) -- [Ollama4j for Java](https://github.com/amithkoujalgi/ollama4j) +- [Ollama4j for Java](https://github.com/ollama4j/ollama4j) - [ModelFusion Typescript Library](https://modelfusion.dev/integration/model-provider/ollama) - [OllamaKit for Swift](https://github.com/kevinhermawan/OllamaKit) - [Ollama for Dart](https://github.com/breitburg/dart-ollama) From 50c05d57e0100625ee5bdd4ba9accec7f4536005 Mon Sep 17 00:00:00 2001 From: Mateusz Migas <54471371+mateuszmigas@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:15:54 +0200 Subject: [PATCH 102/174] readme: add Painting Droid community integration (#5514) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5568b6e7..fee01baa 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [OllamaSpring](https://github.com/CrazyNeil/OllamaSpring) (Ollama Client for macOS) - [LLocal.in](https://github.com/kartikm7/llocal) (Easy to use Electron Desktop Client for Ollama) - [Ollama with Google Mesop](https://github.com/rapidarchitect/ollama_mesop/) (Mesop Chat Client implementation with Ollama) +- [Painting Droid](https://github.com/mateuszmigas/painting-droid) (Painting app with AI integrations) - [Kerlig AI](https://www.kerlig.com/) (AI writing assistant for macOS) - [AI Studio](https://github.com/MindWorkAI/AI-Studio) - [Sidellama](https://github.com/gyopak/sidellama) (browser-based LLM client) From 037a4d103edff143db2f82e1feb8b5b80afea6f1 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 3 Sep 2024 14:55:20 -0700 Subject: [PATCH 103/174] Log system memory at info (#6617) On systems with low system memory, we can hit allocation failures that are difficult to diagnose without debug logs. This will make it easier to spot. --- llm/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llm/server.go b/llm/server.go index c38bc6bb..9c08f1bb 100644 --- a/llm/server.go +++ b/llm/server.go @@ -98,7 +98,7 @@ func NewLlamaServer(gpus gpu.GpuInfoList, model string, ggml *GGML, adapters, pr systemTotalMemory = systemMemInfo.TotalMemory systemFreeMemory = systemMemInfo.FreeMemory systemSwapFreeMemory = systemMemInfo.FreeSwap - slog.Debug("system memory", "total", format.HumanBytes2(systemTotalMemory), "free", format.HumanBytes2(systemFreeMemory), "free_swap", format.HumanBytes2(systemSwapFreeMemory)) + slog.Info("system memory", "total", format.HumanBytes2(systemTotalMemory), "free", format.HumanBytes2(systemFreeMemory), "free_swap", format.HumanBytes2(systemSwapFreeMemory)) } // If the user wants zero GPU layers, reset the gpu list to be CPU/system ram info From f29b167e1af8a7c0e8a15044584826432aff76d8 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Tue, 3 Sep 2024 17:15:31 -0700 Subject: [PATCH 104/174] Use cuda v11 for driver 525 and older (#6620) It looks like driver 525 (aka, cuda driver 12.0) has problems with the cuda v12 library we compile against, so run v11 on those older drivers if detected. --- gpu/cuda_common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpu/cuda_common.go b/gpu/cuda_common.go index 827cc9b4..aceec70a 100644 --- a/gpu/cuda_common.go +++ b/gpu/cuda_common.go @@ -57,7 +57,7 @@ func cudaVariant(gpuInfo CudaGPUInfo) string { } } - if gpuInfo.computeMajor < 6 || gpuInfo.DriverMajor < 12 { + if gpuInfo.computeMajor < 6 || gpuInfo.DriverMajor < 12 || (gpuInfo.DriverMajor == 12 && gpuInfo.DriverMinor == 0) { return "v11" } return "v12" From 5e2653f9fe454e948a8d48e3c15c21830c1ac26b Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 3 Sep 2024 21:12:39 -0400 Subject: [PATCH 105/174] llm: update llama.cpp commit to 8962422 (#6618) --- llm/ext_server/server.cpp | 19 +- llm/generate/gen_darwin.sh | 2 +- llm/llama.cpp | 2 +- llm/patches/05-default-pretokenizer.diff | 10 +- llm/patches/06-embeddings.diff | 28 +- llm/patches/09-lora.diff | 350 ----------------------- llm/patches/11-phi3-sliding-window.diff | 43 --- 7 files changed, 32 insertions(+), 422 deletions(-) delete mode 100644 llm/patches/09-lora.diff delete mode 100644 llm/patches/11-phi3-sliding-window.diff diff --git a/llm/ext_server/server.cpp b/llm/ext_server/server.cpp index 1e230611..fc673c47 100644 --- a/llm/ext_server/server.cpp +++ b/llm/ext_server/server.cpp @@ -425,7 +425,7 @@ struct llama_server_context n_ctx = llama_n_ctx(ctx); - add_bos_token = llama_should_add_bos_token(model); + add_bos_token = llama_add_bos_token(model); return true; } @@ -1031,7 +1031,7 @@ struct llama_server_context continue; } - if (!llava_image_embed_make_with_clip_img(clp_ctx, params.n_threads, img.img_data, &img.image_embedding, &img.image_tokens)) { + if (!llava_image_embed_make_with_clip_img(clp_ctx, params.cpuparams.n_threads, img.img_data, &img.image_embedding, &img.image_tokens)) { LOG_TEE("Error processing the given image"); return false; } @@ -2014,7 +2014,7 @@ static void server_print_usage(const char *argv0, const gpt_params ¶ms, printf("options:\n"); printf(" -h, --help show this help message and exit\n"); printf(" -v, --verbose verbose output (default: %s)\n", server_verbose ? "enabled" : "disabled"); - printf(" -t N, --threads N number of threads to use during computation (default: %d)\n", params.n_threads); + printf(" -t N, --threads N number of threads to use during computation (default: %d)\n", params.cpuparams.n_threads); printf(" -tb N, --threads-batch N number of threads to use during batch and prompt processing (default: same as --threads)\n"); printf(" --threads-http N number of threads in the http server pool to process requests (default: max(hardware concurrency - 1, --parallel N + 2))\n"); printf(" -c N, --ctx-size N size of the prompt context (default: %d)\n", params.n_ctx); @@ -2287,7 +2287,7 @@ static void server_params_parse(int argc, char **argv, server_params &sparams, g invalid_param = true; break; } - params.n_threads = std::stoi(argv[i]); + params.cpuparams.n_threads = std::stoi(argv[i]); } else if (arg == "--grp-attn-n" || arg == "-gan") { @@ -2315,7 +2315,7 @@ static void server_params_parse(int argc, char **argv, server_params &sparams, g invalid_param = true; break; } - params.n_threads_batch = std::stoi(argv[i]); + params.cpuparams_batch.n_threads = std::stoi(argv[i]); } else if (arg == "--threads-http") { @@ -2626,6 +2626,11 @@ static void server_params_parse(int argc, char **argv, server_params &sparams, g params.kv_overrides.back().key[0] = 0; } + postprocess_cpu_params(params.cpuparams, nullptr); + postprocess_cpu_params(params.cpuparams_batch, ¶ms.cpuparams); + postprocess_cpu_params(params.draft_cpuparams, ¶ms.cpuparams); + postprocess_cpu_params(params.draft_cpuparams_batch, ¶ms.cpuparams_batch); + if (invalid_param) { fprintf(stderr, "error: invalid parameter for argument: %s\n", arg.c_str()); @@ -2775,8 +2780,8 @@ int main(int argc, char **argv) { {"commit", LLAMA_COMMIT}}); LOG_INFO("system info", { - {"n_threads", params.n_threads}, - {"n_threads_batch", params.n_threads_batch}, + {"n_threads", params.cpuparams.n_threads}, + {"n_threads_batch", params.cpuparams_batch.n_threads}, {"total_threads", std::thread::hardware_concurrency()}, {"system_info", llama_print_system_info()}, }); diff --git a/llm/generate/gen_darwin.sh b/llm/generate/gen_darwin.sh index f22c0f8e..acea9c8d 100755 --- a/llm/generate/gen_darwin.sh +++ b/llm/generate/gen_darwin.sh @@ -19,7 +19,7 @@ sign() { fi } -COMMON_DARWIN_DEFS="-DBUILD_SHARED_LIBS=off -DCMAKE_OSX_DEPLOYMENT_TARGET=11.3 -DLLAMA_METAL_MACOSX_VERSION_MIN=11.3 -DCMAKE_SYSTEM_NAME=Darwin -DGGML_METAL_EMBED_LIBRARY=on -DGGML_OPENMP=off" +COMMON_DARWIN_DEFS="-DBUILD_SHARED_LIBS=off -DCMAKE_OSX_DEPLOYMENT_TARGET=11.3 -DGGML_METAL_MACOSX_VERSION_MIN=11.3 -DCMAKE_SYSTEM_NAME=Darwin -DGGML_METAL_EMBED_LIBRARY=on -DGGML_OPENMP=off" case "${GOARCH}" in "amd64") diff --git a/llm/llama.cpp b/llm/llama.cpp index 1e6f6554..8962422b 160000 --- a/llm/llama.cpp +++ b/llm/llama.cpp @@ -1 +1 @@ -Subproject commit 1e6f6554aa11fa10160a5fda689e736c3c34169f +Subproject commit 8962422b1c6f9b8b15f5aeaea42600bcc2d44177 diff --git a/llm/patches/05-default-pretokenizer.diff b/llm/patches/05-default-pretokenizer.diff index 0d40fc3c..351bcaef 100644 --- a/llm/patches/05-default-pretokenizer.diff +++ b/llm/patches/05-default-pretokenizer.diff @@ -1,8 +1,8 @@ diff --git a/src/llama.cpp b/src/llama.cpp -index a207451f..2ddf431d 100644 +index 88355971..dd7d41ed 100644 --- a/src/llama.cpp +++ b/src/llama.cpp -@@ -5347,16 +5347,7 @@ static void llm_load_vocab( +@@ -6083,16 +6083,7 @@ static void llm_load_vocab( if (vocab.type == LLAMA_VOCAB_TYPE_BPE) { vocab.tokenizer_add_space_prefix = false; vocab.tokenizer_clean_spaces = true; @@ -20,9 +20,9 @@ index a207451f..2ddf431d 100644 vocab.type_pre = LLAMA_VOCAB_PRE_TYPE_DEFAULT; } else if ( tokenizer_pre == "llama3" || -@@ -5443,7 +5434,8 @@ static void llm_load_vocab( - tokenizer_pre == "codeshell") { - vocab.type_pre = LLAMA_VOCAB_PRE_TYPE_CODESHELL; +@@ -6188,7 +6179,8 @@ static void llm_load_vocab( + tokenizer_pre == "exaone") { + vocab.type_pre = LLAMA_VOCAB_PRE_TYPE_EXAONE; } else { - throw std::runtime_error(format("unknown pre-tokenizer type: '%s'", tokenizer_pre.c_str())); + LLAMA_LOG_WARN("%s: missing or unrecognized pre-tokenizer type, using: 'default'\n", __func__); diff --git a/llm/patches/06-embeddings.diff b/llm/patches/06-embeddings.diff index a84e3b06..f3c071cb 100644 --- a/llm/patches/06-embeddings.diff +++ b/llm/patches/06-embeddings.diff @@ -1,37 +1,36 @@ diff --git a/src/llama.cpp b/src/llama.cpp -index 1fe2b9f7..a43312a7 100644 +index 88355971..d7db689b 100644 --- a/src/llama.cpp +++ b/src/llama.cpp -@@ -13689,7 +13689,7 @@ static size_t llama_output_reserve(llama_context & lctx, size_t n_outputs) { +@@ -15906,7 +15906,7 @@ static size_t llama_output_reserve(llama_context & lctx, size_t n_outputs) { const auto n_embd = hparams.n_embd; // TODO: use a per-batch flag for logits presence instead - const bool has_logits = !cparams.embeddings; + const bool has_logits = cparams.causal_attn; - const bool has_embd = lctx.is_encoding || (cparams.embeddings && (cparams.pooling_type == LLAMA_POOLING_TYPE_NONE)); + const bool has_embd = cparams.embeddings && (cparams.pooling_type == LLAMA_POOLING_TYPE_NONE); const size_t logits_size = has_logits ? n_vocab*n_outputs_max : 0; -@@ -13959,17 +13959,25 @@ static int llama_decode_internal( +@@ -16175,20 +16175,23 @@ static int llama_decode_internal( // no output res = nullptr; embd = nullptr; - } else if (cparams.embeddings) { -- res = nullptr; // do not extract logits for embedding case -- embd = gf->nodes[gf->n_nodes - 1]; -- if (strcmp(embd->name, "result_embd_pooled") != 0) { -- embd = gf->nodes[gf->n_nodes - 2]; +- res = nullptr; // do not extract logits for embedding case +- embd = nullptr; + } + + if (cparams.embeddings) { -+ for (int i = gf->n_nodes - 1; i >= 0; --i) { + for (int i = gf->n_nodes - 1; i >= 0; --i) { +- if (strcmp(gf->nodes[i]->name, "result_embd_pooled") == 0) { +- embd = gf->nodes[i]; + embd = gf->nodes[i]; + if (strcmp(embd->name, "result_embd_pooled") == 0) { -+ break; -+ } + break; + } } - GGML_ASSERT(strcmp(embd->name, "result_embd_pooled") == 0 && "missing embeddings tensor"); -- } else { -+ } else { +- GGML_ASSERT(embd != nullptr && "missing embeddings tensor"); + } else { embd = nullptr; // do not extract embeddings when not needed GGML_ASSERT(strcmp(res->name, "result_output") == 0 && "missing result_output tensor"); } @@ -39,7 +38,6 @@ index 1fe2b9f7..a43312a7 100644 + if (!cparams.causal_attn) { + res = nullptr; // do not extract logits when not needed + } -+ // LLAMA_LOG_INFO("graph build time: %.3f ms (%d nodes, %d leafs)\n", (ggml_time_us() - t_start_us)/1000.0, gf->n_nodes, gf->n_leafs); ggml_backend_sched_alloc_graph(lctx.sched, gf); diff --git a/llm/patches/09-lora.diff b/llm/patches/09-lora.diff deleted file mode 100644 index 21958476..00000000 --- a/llm/patches/09-lora.diff +++ /dev/null @@ -1,350 +0,0 @@ -diff --git a/common/common.cpp b/common/common.cpp -index 2e8374d5..70d0afde 100644 ---- a/common/common.cpp -+++ b/common/common.cpp -@@ -2110,9 +2110,21 @@ struct llama_init_result llama_init_from_gpt_params(gpt_params & params) { - loaded_la.adapter = llama_lora_adapter_init(model, la.path.c_str()); - if (loaded_la.adapter == nullptr) { - fprintf(stderr, "%s: error: failed to apply lora adapter '%s'\n", __func__, la.path.c_str()); -- llama_free(lctx); -- llama_free_model(model); -- return iparams; -+ -+ // if that fails, try loading as ggla for compatibility -+ int err = llama_model_apply_lora_from_file(model, -+ la.path.c_str(), -+ la.scale, -+ nullptr, -+ params.n_threads); -+ if (err != 0) { -+ fprintf(stderr, "%s: error: failed to apply lora adapter\n", __func__); -+ llama_free(lctx); -+ llama_free_model(model); -+ return iparams; -+ } else { -+ break; -+ } - } - iparams.lora_adapters.push_back(loaded_la); // copy to list of loaded adapters - } -diff --git a/include/llama.h b/include/llama.h -index 93fd77ca..b0fb37a6 100644 ---- a/include/llama.h -+++ b/include/llama.h -@@ -1160,6 +1160,20 @@ extern "C" { - - LLAMA_API void llama_dump_timing_info_yaml(FILE * stream, const struct llama_context * ctx); - -+ // Apply a LoRA adapter to a loaded model -+ // path_base_model is the path to a higher quality model to use as a base for -+ // the layers modified by the adapter. Can be NULL to use the current loaded model. -+ // The model needs to be reloaded before applying a new adapter, otherwise the adapter -+ // will be applied on top of the previous one -+ // Returns 0 on success -+ LLAMA_API int32_t llama_model_apply_lora_from_file( -+ const struct llama_model * model, -+ const char * path_lora, -+ float scale, -+ const char * path_base_model, -+ int32_t n_threads); -+ -+ - #ifdef __cplusplus - } - #endif -diff --git a/src/llama.cpp b/src/llama.cpp -index 80a0dd0f..9d7b0e17 100644 ---- a/src/llama.cpp -+++ b/src/llama.cpp -@@ -21880,3 +21880,290 @@ static void llama_log_callback_default(ggml_log_level level, const char * text, - fputs(text, stderr); - fflush(stderr); - } -+ -+static int llama_apply_lora_from_file_internal( -+ const struct llama_model & model, const char * path_lora, float scale, const char * path_base_model, int n_threads -+) { -+ LLAMA_LOG_INFO("%s: applying lora adapter from '%s' - please wait ...\n", __func__, path_lora); -+ -+ const int64_t t_start_lora_us = ggml_time_us(); -+ -+ llama_file fin(path_lora, "rb"); -+ -+ // verify magic and version -+ { -+ uint32_t magic = fin.read_u32(); -+ if (magic != LLAMA_FILE_MAGIC_GGLA) { -+ LLAMA_LOG_ERROR("%s: bad file magic\n", __func__); -+ return 1; -+ } -+ -+ uint32_t format_version = fin.read_u32(); -+ if (format_version != 1) { -+ LLAMA_LOG_ERROR("%s: unsupported file version\n", __func__ ); -+ return 1; -+ } -+ } -+ -+ int32_t lora_r = fin.read_u32(); -+ int32_t lora_alpha = fin.read_u32(); -+ float scaling = scale * (float)lora_alpha / (float)lora_r; -+ -+ LLAMA_LOG_INFO("%s: r = %d, alpha = %d, scaling = %.2f\n", __func__, lora_r, lora_alpha, scaling); -+ -+ // load base model -+ std::unique_ptr ml; -+ if (path_base_model) { -+ LLAMA_LOG_INFO("%s: loading base model from '%s'\n", __func__, path_base_model); -+ ml.reset(new llama_model_loader(path_base_model, /*use_mmap*/ true, /*check_tensors*/ false, /*kv_overrides*/ nullptr)); -+ ml->init_mappings(/*prefetch*/ false); // no prefetching -+ } -+ -+ struct tensor_meta { -+ std::string name; -+ ggml_type type; -+ int32_t ne[2]; -+ size_t offset; -+ }; -+ std::map tensor_meta_map; -+ -+ // load all tensor meta -+ while (true) { -+ if (fin.tell() == fin.size) { -+ // eof -+ break; -+ } -+ -+ int32_t n_dims; -+ int32_t name_len; -+ int32_t ftype; -+ -+ fin.read_raw(&n_dims, sizeof(n_dims)); -+ fin.read_raw(&name_len, sizeof(name_len)); -+ fin.read_raw(&ftype, sizeof(ftype)); -+ -+ if (n_dims != 1 && n_dims != 2) { -+ LLAMA_LOG_ERROR("%s: unsupported tensor dimension %d\n", __func__, n_dims); -+ return 1; -+ } -+ -+ int32_t ne[2] = { 1, 1 }; -+ for (int i = 0; i < n_dims; ++i) { -+ fin.read_raw(&ne[i], sizeof(ne[i])); -+ } -+ -+ std::string name; -+ { -+ GGML_ASSERT(name_len < GGML_MAX_NAME); -+ char buf[GGML_MAX_NAME]; -+ fin.read_raw(buf, name_len); -+ name = std::string(buf, name_len); -+ } -+ -+ // check for lora suffix -+ std::string lora_suffix; -+ if (name.length() > 6) { -+ lora_suffix = name.substr(name.length() - 6); -+ } -+ if (lora_suffix != ".loraA" && lora_suffix != ".loraB") { -+ LLAMA_LOG_ERROR("%s: error: '%s' is not a lora tensor\n", __func__, name.c_str()); -+ return 1; -+ } -+ -+ // tensor type -+ ggml_type wtype; -+ switch (ftype) { -+ case 0: wtype = GGML_TYPE_F32; break; -+ case 1: wtype = GGML_TYPE_F16; break; -+ default: -+ { -+ LLAMA_LOG_ERROR("%s: invalid tensor data type '%d'\n", -+ __func__, ftype); -+ return 1; -+ } -+ } -+ -+ // data offset -+ size_t offset = fin.tell(); -+ offset = (offset + 31) & -32; -+ -+ // skip tensor data -+ fin.seek(offset + ggml_row_size(wtype, ne[0]) * ne[1], SEEK_SET); -+ -+ tensor_meta_map.emplace(name, tensor_meta{ name, wtype, { ne[0], ne[1] }, offset }); -+ } -+ -+ bool warned = false; -+ int n_tensors = 0; -+ -+ // apply -+ ggml_backend_t backend_cpu = ggml_backend_cpu_init(); -+ if (backend_cpu == nullptr) { -+ LLAMA_LOG_ERROR("%s: error: failed to initialize cpu backend\n", __func__); -+ return 1; -+ } -+ ggml_backend_cpu_set_n_threads(backend_cpu, n_threads); -+ -+ std::vector> read_buf; -+ for (const auto & it : model.tensors_by_name) { -+ const std::string & base_name = it.first; -+ ggml_tensor * model_t = it.second; -+ -+ if (tensor_meta_map.find(base_name + ".loraA") == tensor_meta_map.end() || -+ tensor_meta_map.find(base_name + ".loraB") == tensor_meta_map.end()) { -+ continue; -+ } -+ -+ tensor_meta & metaA = tensor_meta_map.at(base_name + ".loraA"); -+ tensor_meta & metaB = tensor_meta_map.at(base_name + ".loraB"); -+ -+ ggml_init_params lora_init_params = { -+ /* .mem_size */ ggml_tensor_overhead()*128 + ggml_graph_overhead(), -+ /* .mem_buffer */ nullptr, -+ /* .no_alloc */ true, -+ }; -+ ggml_context * lora_ctx = ggml_init(lora_init_params); -+ if (lora_ctx == nullptr) { -+ LLAMA_LOG_ERROR("%s: error: failed to initialize lora context\n", __func__); -+ ggml_backend_free(backend_cpu); -+ return 1; -+ } -+ -+ // create tensors -+ ggml_tensor * loraA = ggml_new_tensor_2d(lora_ctx, metaA.type, metaA.ne[0], metaA.ne[1]); -+ ggml_tensor * loraB = ggml_new_tensor_2d(lora_ctx, metaB.type, metaB.ne[0], metaB.ne[1]); -+ ggml_set_name(loraA, metaA.name.c_str()); -+ ggml_set_name(loraB, metaB.name.c_str()); -+ -+ ggml_tensor * base_t; -+ if (ml) { -+ if (!ml->get_tensor_meta(base_name.c_str())) { -+ LLAMA_LOG_ERROR("%s: error: tensor '%s' not found in base model\n", __func__, base_name.c_str()); -+ return 1; -+ } -+ base_t = ggml_dup_tensor(lora_ctx, ml->get_tensor_meta(base_name.c_str())); -+ } else { -+ base_t = ggml_dup_tensor(lora_ctx, model_t); -+ } -+ ggml_set_name(base_t, base_name.c_str()); -+ -+ // allocate in backend buffer -+ ggml_backend_buffer_t lora_buf = ggml_backend_alloc_ctx_tensors_from_buft(lora_ctx, ggml_backend_cpu_buffer_type()); -+ if (lora_buf == nullptr) { -+ LLAMA_LOG_ERROR("%s: error: failed to allocate lora tensors\n", __func__); -+ return 1; -+ } -+ -+ // load tensor data -+ auto load_tensor = [&read_buf, &fin](const tensor_meta & tensor_meta, ggml_tensor * tensor) { -+ read_buf.resize(ggml_nbytes(tensor)); -+ fin.seek(tensor_meta.offset, SEEK_SET); -+ fin.read_raw(read_buf.data(), ggml_nbytes(tensor)); -+ ggml_backend_tensor_set(tensor, read_buf.data(), 0, read_buf.size()); -+ }; -+ load_tensor(metaA, loraA); -+ load_tensor(metaB, loraB); -+ -+ // load base model tensor data -+ if (ml) { -+ ml->load_data_for(base_t); -+ } else { -+ ggml_backend_tensor_copy(model_t, base_t); -+ } -+ -+ if (ggml_is_quantized(base_t->type) && !warned) { -+ LLAMA_LOG_WARN("%s: warning: using a lora adapter with a quantized model may result in poor quality, " -+ "use a f16 or f32 base model with --lora-base\n", __func__); -+ warned = true; -+ } -+ -+ if (base_t->ne[0] != loraA->ne[1] || base_t->ne[1] != loraB->ne[1]) { -+ LLAMA_LOG_ERROR("%s: incompatible tensor dimensions (%" PRId64 " and %" PRId64 ");" -+ " are you sure that this adapter is for this model?\n", __func__, base_t->ne[0], loraA->ne[1]); -+ ggml_free(lora_ctx); -+ ggml_backend_buffer_free(lora_buf); -+ ggml_backend_free(backend_cpu); -+ return 1; -+ } -+ -+ auto build_lora_graph = [&]() { -+ // w = w + BA*s -+ ggml_tensor * BA = ggml_mul_mat(lora_ctx, loraA, loraB); -+ ggml_set_name(BA, "BA"); -+ -+ if (scaling != 1.0f) { -+ BA = ggml_scale(lora_ctx, BA, scaling); -+ ggml_set_name(BA, "BA_scaled"); -+ } -+ -+ ggml_tensor * r; -+ r = ggml_add_inplace(lora_ctx, base_t, BA); -+ ggml_set_name(r, "r_add"); -+ -+ if (base_t->type != model_t->type) { -+ // convert the result to the model type -+ r = ggml_cast(lora_ctx, r, model_t->type); -+ ggml_set_name(r, "r_cast"); -+ } -+ -+ return r; -+ }; -+ -+ ggml_cgraph * gf = ggml_new_graph(lora_ctx); -+ ggml_tensor * r = build_lora_graph(); -+ ggml_build_forward_expand(gf, r); -+ -+ ggml_backend_buffer_t graph_buf = ggml_backend_alloc_ctx_tensors_from_buft(lora_ctx, ggml_backend_cpu_buffer_type()); -+ if (graph_buf == nullptr) { -+ LLAMA_LOG_ERROR("%s: error: failed to allocate graph tensors\n", __func__); -+ ggml_free(lora_ctx); -+ ggml_backend_buffer_free(lora_buf); -+ ggml_backend_free(backend_cpu); -+ return 1; -+ } -+ -+ ggml_backend_graph_compute(backend_cpu, gf); -+ -+ ggml_backend_tensor_set(model_t, r->data, 0, ggml_nbytes(r)); -+ -+#if 0 -+ // TODO: use scheduler with fallback to CPU for less copies between CPU and GPU -+ //ggml_backend_sched_t sched = ggml_backend_sched_new(backends.data(), backends.size(), GGML_DEFAULT_GRAPH_SIZE); -+ -+ // sched compute -+ ggml_build_forward_expand(gf, build_graph()); -+ ggml_backend_sched_init_measure(sched, gf); -+ -+ // create the graph again, since the previous one was destroyed by the measure -+ ggml_graph_clear(gf); -+ ggml_build_forward_expand(gf, build_graph()); -+ ggml_backend_sched_graph_compute(sched, gf); -+ ggml_backend_sched_free(sched); -+#endif -+ -+ ggml_backend_buffer_free(lora_buf); -+ ggml_backend_buffer_free(graph_buf); -+ ggml_free(lora_ctx); -+ -+ n_tensors++; -+ if (n_tensors % 4 == 0) { -+ LLAMA_LOG_INFO("."); -+ } -+ } -+ -+ ggml_backend_free(backend_cpu); -+ -+ const int64_t t_lora_us = ggml_time_us() - t_start_lora_us; -+ LLAMA_LOG_INFO(" done (%.2f ms)\n", t_lora_us / 1000.0); -+ -+ return 0; -+} -+ -+int32_t llama_model_apply_lora_from_file(const struct llama_model * model, const char * path_lora, float scale, const char * path_base_model, int32_t n_threads) { -+ try { -+ return llama_apply_lora_from_file_internal(*model, path_lora, scale, path_base_model, n_threads); -+ } catch (const std::exception & err) { -+ LLAMA_LOG_ERROR("%s: failed to apply lora adapter: %s\n", __func__, err.what()); -+ return 1; -+ } -+} -\ No newline at end of file \ No newline at end of file diff --git a/llm/patches/11-phi3-sliding-window.diff b/llm/patches/11-phi3-sliding-window.diff deleted file mode 100644 index fde3dd21..00000000 --- a/llm/patches/11-phi3-sliding-window.diff +++ /dev/null @@ -1,43 +0,0 @@ -From 6eedae4cf2fcc8015dac79cb3f28f61fcabacab2 Mon Sep 17 00:00:00 2001 -From: Michael Yang -Date: Wed, 31 Jul 2024 14:57:04 -0700 -Subject: [PATCH] phi3 sliding window - ---- - src/llama.cpp | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/llama.cpp b/src/llama.cpp -index a207451f..f2872d4e 100644 ---- a/src/llama.cpp -+++ b/src/llama.cpp -@@ -4893,7 +4893,7 @@ static void llm_load_hparams( - } break; - case LLM_ARCH_PHI3: - { -- ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa); -+ ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa, false); - ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps); - - switch (hparams.n_layer) { -@@ -10762,7 +10762,7 @@ struct llm_build_context { - struct ggml_tensor * inp_pos = build_inp_pos(); - - // KQ_mask (mask for 1 head, it will be broadcasted to all heads) -- struct ggml_tensor * KQ_mask_swa = build_inp_KQ_mask_swa(); -+ struct ggml_tensor * KQ_mask = hparams.n_swa > 0 ? build_inp_KQ_mask_swa() : build_inp_KQ_mask(); - - for (int il = 0; il < n_layer; ++il) { - auto residual = inpL; -@@ -10820,7 +10820,7 @@ struct llm_build_context { - - cur = llm_build_kv(ctx0, lctx, kv_self, gf, - model.layers[il].wo, model.layers[il].bo, -- Kcur, Vcur, Qcur, KQ_mask_swa, n_tokens, kv_head, n_kv, 1.0f, cb, il); -+ Kcur, Vcur, Qcur, KQ_mask, n_tokens, kv_head, n_kv, 1.0f, cb, il); - } - - if (il == n_layer - 1) { --- -2.45.2 - From 491fc312ae1e5a7a2ea0a02546b2ffa61675dd1a Mon Sep 17 00:00:00 2001 From: Viz <7476271+kspviswa@users.noreply.github.com> Date: Tue, 3 Sep 2024 23:10:53 -0400 Subject: [PATCH 106/174] readme: add PyOllaMx project (#6624) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fee01baa..4fc02d10 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,8 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Go-CREW](https://www.jonathanhecl.com/go-crew/) (Powerful Offline RAG in Golang) - [PartCAD](https://github.com/openvmp/partcad/) (CAD model generation with OpenSCAD and CadQuery) - [Ollama4j Web UI](https://github.com/ollama4j/ollama4j-web-ui) - Java-based Web UI for Ollama built with Vaadin, Spring Boot and Ollama4j - +- [PyOllaMx](https://github.com/kspviswa/pyOllaMx) - macOS application capable of chatting with both Ollama and Apple MLX models. + ### Terminal - [oterm](https://github.com/ggozad/oterm) From 27bcce6d9fb1e002ef4393ff48d6cadb5b29da41 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 4 Sep 2024 23:32:26 +1000 Subject: [PATCH 107/174] readme: add claude-dev to community integrations (#6630) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4fc02d10..edd8ab1b 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [PartCAD](https://github.com/openvmp/partcad/) (CAD model generation with OpenSCAD and CadQuery) - [Ollama4j Web UI](https://github.com/ollama4j/ollama4j-web-ui) - Java-based Web UI for Ollama built with Vaadin, Spring Boot and Ollama4j - [PyOllaMx](https://github.com/kspviswa/pyOllaMx) - macOS application capable of chatting with both Ollama and Apple MLX models. +- [Claude Dev](https://github.com/saoudrizwan/claude-dev) - VSCode extension for multi-file/whole-repo coding ### Terminal From 7d89e48f5c1a8e719d5b8d154151c39685f50919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 4 Sep 2024 16:34:53 +0300 Subject: [PATCH 108/174] install.sh: update instructions to use WSL2 (#6450) --- scripts/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install.sh b/scripts/install.sh index 25f57565..5a212975 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -38,7 +38,7 @@ IS_WSL2=false KERN=$(uname -r) case "$KERN" in *icrosoft*WSL2 | *icrosoft*wsl2) IS_WSL2=true;; - *icrosoft) error "Microsoft WSL1 is not currently supported. Please upgrade to WSL2 with 'wsl --set-version 2'" ;; + *icrosoft) error "Microsoft WSL1 is not currently supported. Please use WSL2 with 'wsl --set-version 2'" ;; *) ;; esac From 369479cc3068b5f2b28cdc766de3c27cff7e2f7b Mon Sep 17 00:00:00 2001 From: Carter <102479896+Carter907@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:42:33 -0400 Subject: [PATCH 109/174] docs: fix spelling error (#6391) change "dorrect" to "correct" --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f12a0fc..f003a69d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ See the [development documentation](./docs/development.md) for instructions on h * New features: new features (e.g. API fields, environment variables) add surface area to Ollama and make it harder to maintain in the long run as they cannot be removed without potentially breaking users in the future. * Refactoring: large code improvements are important, but can be harder or take longer to review and merge. -* Documentation: small updates to fill in or dorrect missing documentation is helpful, however large documentation additions can be hard to maintain over time. +* Documentation: small updates to fill in or correct missing documentation is helpful, however large documentation additions can be hard to maintain over time. ### Issues that may not be accepted From 93eb43d020665cce0f1de17cc108224ae83b0605 Mon Sep 17 00:00:00 2001 From: Mitar Date: Wed, 4 Sep 2024 16:52:46 +0200 Subject: [PATCH 110/174] readme: add Go fun package (#6421) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index edd8ab1b..a6f254fe 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [PromptingTools.jl](https://github.com/svilupp/PromptingTools.jl) with an [example](https://svilupp.github.io/PromptingTools.jl/dev/examples/working_with_ollama) - [LlamaScript](https://github.com/Project-Llama/llamascript) - [Ollamaclient for Golang](https://github.com/xyproto/ollamaclient) +- [High-level function abstraction in Go](https://gitlab.com/tozd/go/fun) ### Mobile From 5b55379651f4cdbd5efcc397d894c3d008c96a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=A2=E5=A5=8B=E7=8C=AB?= Date: Wed, 4 Sep 2024 22:53:36 +0800 Subject: [PATCH 111/174] readme: add Cherry Studio to community integrations (#6633) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a6f254fe..afcda18f 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Ollama4j Web UI](https://github.com/ollama4j/ollama4j-web-ui) - Java-based Web UI for Ollama built with Vaadin, Spring Boot and Ollama4j - [PyOllaMx](https://github.com/kspviswa/pyOllaMx) - macOS application capable of chatting with both Ollama and Apple MLX models. - [Claude Dev](https://github.com/saoudrizwan/claude-dev) - VSCode extension for multi-file/whole-repo coding +- [Cherry Studio](https://github.com/kangfenmao/cherry-studio) (Desktop client with Ollama support) ### Terminal From f36ebfb47835075e3b9d01a6c1a082653c24d9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Te=C3=AFlo=20M?= <39594466+teilomillet@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:19:41 +0200 Subject: [PATCH 112/174] readme: add gollm to the list of community libraries (#6099) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index afcda18f..56f2e1d8 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Portkey](https://portkey.ai/docs/welcome/integration-guides/ollama) - [PromptingTools.jl](https://github.com/svilupp/PromptingTools.jl) with an [example](https://svilupp.github.io/PromptingTools.jl/dev/examples/working_with_ollama) - [LlamaScript](https://github.com/Project-Llama/llamascript) +- [Gollm](https://docs.gollm.co/examples/ollama-example) - [Ollamaclient for Golang](https://github.com/xyproto/ollamaclient) - [High-level function abstraction in Go](https://gitlab.com/tozd/go/fun) From 133770a5484e4ff2e22a49b4728a4ddac7e19db4 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Wed, 4 Sep 2024 11:45:09 -0700 Subject: [PATCH 113/174] docs: add group to manual Linux isntructions and verify service is running (#6430) --- docs/linux.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/linux.md b/docs/linux.md index fbaf4845..46c17a20 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -35,10 +35,11 @@ curl -fsSL https://ollama.com/download/ollama-linux-amd64-rocm.tgz | sudo tar zx ### Adding Ollama as a startup service (recommended) -Create a user for Ollama: +Create a user and group for Ollama: ```bash -sudo useradd -r -s /bin/false -m -d /usr/share/ollama ollama +sudo useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama +sudo usermod -a -G ollama $(whoami) ``` Create a service file in `/etc/systemd/system/ollama.service`: @@ -54,6 +55,7 @@ User=ollama Group=ollama Restart=always RestartSec=3 +Environment="PATH=$PATH" [Install] WantedBy=default.target @@ -83,10 +85,11 @@ Make sure to install ROCm v6 ### Start Ollama -Start Ollama using `systemd`: +Start Ollama and verify it is running: ```bash sudo systemctl start ollama +sudo systemctl status ollama ``` ## Update From c18ff18b2c95a8a39dcd7148c0311e8409221ec5 Mon Sep 17 00:00:00 2001 From: Rune Berg <1runeberg@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:26:02 +1200 Subject: [PATCH 114/174] readme: add confichat to community integrations (#6378) --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 56f2e1d8..ea279761 100644 --- a/README.md +++ b/README.md @@ -309,7 +309,8 @@ See the [API documentation](./docs/api.md) for all endpoints. - [PyOllaMx](https://github.com/kspviswa/pyOllaMx) - macOS application capable of chatting with both Ollama and Apple MLX models. - [Claude Dev](https://github.com/saoudrizwan/claude-dev) - VSCode extension for multi-file/whole-repo coding - [Cherry Studio](https://github.com/kangfenmao/cherry-studio) (Desktop client with Ollama support) - +- [ConfiChat](https://github.com/1runeberg/confichat) (Lightweight, standalone, multi-platform, and privacy focused LLM chat interface with optional encryption) + ### Terminal - [oterm](https://github.com/ggozad/oterm) @@ -386,6 +387,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Enchanted](https://github.com/AugustDev/enchanted) - [Maid](https://github.com/Mobile-Artificial-Intelligence/maid) +- [ConfiChat](https://github.com/1runeberg/confichat) (Lightweight, standalone, multi-platform, and privacy focused LLM chat interface with optional encryption) ### Extensions & Plugins From bbe7b96ded600fa429639c9de97f5334c414b6c8 Mon Sep 17 00:00:00 2001 From: Pascal Patry Date: Wed, 4 Sep 2024 19:34:42 -0400 Subject: [PATCH 115/174] llm: use json.hpp from common (#6642) --- llm/ext_server/CMakeLists.txt | 2 +- llm/ext_server/json.hpp | 24596 -------------------------------- 2 files changed, 1 insertion(+), 24597 deletions(-) delete mode 100644 llm/ext_server/json.hpp diff --git a/llm/ext_server/CMakeLists.txt b/llm/ext_server/CMakeLists.txt index 90fd0ef2..88c8b03d 100644 --- a/llm/ext_server/CMakeLists.txt +++ b/llm/ext_server/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET ollama_llama_server) option(LLAMA_SERVER_VERBOSE "Build verbose logging option for Server" ON) set(LLAMA_SERVER_LDFLAGS $ENV{LLAMA_SERVER_LDFLAGS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -add_executable(${TARGET} server.cpp utils.hpp json.hpp httplib.h) +add_executable(${TARGET} server.cpp utils.hpp httplib.h) install(TARGETS ${TARGET} RUNTIME) target_compile_definitions(${TARGET} PRIVATE SERVER_VERBOSE=$ diff --git a/llm/ext_server/json.hpp b/llm/ext_server/json.hpp deleted file mode 100644 index ea945f34..00000000 --- a/llm/ext_server/json.hpp +++ /dev/null @@ -1,24596 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -/****************************************************************************\ - * Note on documentation: The source files contain links to the online * - * documentation of the public API at https://json.nlohmann.me. This URL * - * contains the most recent documentation and should also be applicable to * - * previous versions; documentation for deprecated functions is not * - * removed, but marked deprecated. See "Generate documentation" section in * - * file docs/README.md. * -\****************************************************************************/ - -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ - -#include // all_of, find, for_each -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#ifndef JSON_NO_IO - #include // istream, ostream -#endif // JSON_NO_IO -#include // random_access_iterator_tag -#include // unique_ptr -#include // accumulate -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK - #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 - #warning "Already included a different version of the library!" - #endif - #endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION - #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) - -#define NLOHMANN_JSON_ABI_TAGS \ - NLOHMANN_JSON_ABI_TAGS_CONCAT( \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ - _v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ - NLOHMANN_JSON_VERSION_MINOR, \ - NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ - NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ - nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ - namespace nlohmann \ - { \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_END -#define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ - } // namespace nlohmann -#endif - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // transform -#include // array -#include // forward_list -#include // inserter, front_inserter, end -#include // map -#include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // nullptr_t -#include // exception -#include // runtime_error -#include // to_string -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // uint8_t -#include // string - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // declval, pair -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// https://en.cppreference.com/w/cpp/experimental/is_detected -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - - -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson -// SPDX-License-Identifier: MIT - -/* Hedley - https://nemequ.github.io/hedley - * Created by Evan Nemerson - */ - -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) -#if defined(JSON_HEDLEY_VERSION) - #undef JSON_HEDLEY_VERSION -#endif -#define JSON_HEDLEY_VERSION 15 - -#if defined(JSON_HEDLEY_STRINGIFY_EX) - #undef JSON_HEDLEY_STRINGIFY_EX -#endif -#define JSON_HEDLEY_STRINGIFY_EX(x) #x - -#if defined(JSON_HEDLEY_STRINGIFY) - #undef JSON_HEDLEY_STRINGIFY -#endif -#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) - -#if defined(JSON_HEDLEY_CONCAT_EX) - #undef JSON_HEDLEY_CONCAT_EX -#endif -#define JSON_HEDLEY_CONCAT_EX(a,b) a##b - -#if defined(JSON_HEDLEY_CONCAT) - #undef JSON_HEDLEY_CONCAT -#endif -#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) - -#if defined(JSON_HEDLEY_CONCAT3_EX) - #undef JSON_HEDLEY_CONCAT3_EX -#endif -#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - -#if defined(JSON_HEDLEY_CONCAT3) - #undef JSON_HEDLEY_CONCAT3 -#endif -#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - -#if defined(JSON_HEDLEY_VERSION_ENCODE) - #undef JSON_HEDLEY_VERSION_ENCODE -#endif -#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) - #undef JSON_HEDLEY_VERSION_DECODE_MAJOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) - #undef JSON_HEDLEY_VERSION_DECODE_MINOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) - #undef JSON_HEDLEY_VERSION_DECODE_REVISION -#endif -#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) - -#if defined(JSON_HEDLEY_GNUC_VERSION) - #undef JSON_HEDLEY_GNUC_VERSION -#endif -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) - #undef JSON_HEDLEY_GNUC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION) - #undef JSON_HEDLEY_MSVC_VERSION -#endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) - #undef JSON_HEDLEY_MSVC_VERSION_CHECK -#endif -#if !defined(JSON_HEDLEY_MSVC_VERSION) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION) - #undef JSON_HEDLEY_INTEL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_VERSION) - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #undef JSON_HEDLEY_INTEL_CL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) - #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION) - #undef JSON_HEDLEY_PGI_VERSION -#endif -#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) - #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) - #undef JSON_HEDLEY_PGI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PGI_VERSION) - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #undef JSON_HEDLEY_SUNPRO_VERSION -#endif -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) - #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION -#endif -#if defined(__EMSCRIPTEN__) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION) - #undef JSON_HEDLEY_ARM_VERSION -#endif -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) - #undef JSON_HEDLEY_ARM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_ARM_VERSION) - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION) - #undef JSON_HEDLEY_IBM_VERSION -#endif -#if defined(__ibmxl__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) - #undef JSON_HEDLEY_IBM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IBM_VERSION) - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION) - #undef JSON_HEDLEY_TI_VERSION -#endif -#if \ - defined(__TI_COMPILER_VERSION__) && \ - ( \ - defined(__TMS470__) || defined(__TI_ARM__) || \ - defined(__MSP430__) || \ - defined(__TMS320C2000__) \ - ) -#if (__TI_COMPILER_VERSION__ >= 16000000) - #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif -#endif - -#if defined(JSON_HEDLEY_TI_VERSION_CHECK) - #undef JSON_HEDLEY_TI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_VERSION) - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #undef JSON_HEDLEY_TI_CL2000_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) - #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #undef JSON_HEDLEY_TI_CL430_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) - #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #undef JSON_HEDLEY_TI_ARMCL_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) - #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) - #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #undef JSON_HEDLEY_TI_CL6X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) - #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #undef JSON_HEDLEY_TI_CL7X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) - #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #undef JSON_HEDLEY_TI_CLPRU_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) - #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION) - #undef JSON_HEDLEY_CRAY_VERSION -#endif -#if defined(_CRAYC) - #if defined(_RELEASE_PATCHLEVEL) - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) - #else - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) - #undef JSON_HEDLEY_CRAY_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_CRAY_VERSION) - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION) - #undef JSON_HEDLEY_IAR_VERSION -#endif -#if defined(__IAR_SYSTEMS_ICC__) - #if __VER__ > 1000 - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) - #else - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) - #undef JSON_HEDLEY_IAR_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IAR_VERSION) - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION) - #undef JSON_HEDLEY_TINYC_VERSION -#endif -#if defined(__TINYC__) - #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) - #undef JSON_HEDLEY_TINYC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION) - #undef JSON_HEDLEY_DMC_VERSION -#endif -#if defined(__DMC__) - #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) - #undef JSON_HEDLEY_DMC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_DMC_VERSION) - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #undef JSON_HEDLEY_COMPCERT_VERSION -#endif -#if defined(__COMPCERT_VERSION__) - #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) - #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION) - #undef JSON_HEDLEY_PELLES_VERSION -#endif -#if defined(__POCC__) - #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) - #undef JSON_HEDLEY_PELLES_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PELLES_VERSION) - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #undef JSON_HEDLEY_MCST_LCC_VERSION -#endif -#if defined(__LCC__) && defined(__LCC_MINOR__) - #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) - #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION) - #undef JSON_HEDLEY_GCC_VERSION -#endif -#if \ - defined(JSON_HEDLEY_GNUC_VERSION) && \ - !defined(__clang__) && \ - !defined(JSON_HEDLEY_INTEL_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_ARM_VERSION) && \ - !defined(JSON_HEDLEY_CRAY_VERSION) && \ - !defined(JSON_HEDLEY_TI_VERSION) && \ - !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ - !defined(__COMPCERT__) && \ - !defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_ATTRIBUTE -#endif -#if \ - defined(__has_attribute) && \ - ( \ - (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ - ) -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) -#else -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE -#endif -#if \ - defined(__has_cpp_attribute) && \ - defined(__cplusplus) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS -#endif -#if !defined(__cplusplus) || !defined(__has_cpp_attribute) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#elif \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ - (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_BUILTIN) - #undef JSON_HEDLEY_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) -#else - #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) - #undef JSON_HEDLEY_GNUC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) - #undef JSON_HEDLEY_GCC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_FEATURE) - #undef JSON_HEDLEY_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) -#else - #define JSON_HEDLEY_HAS_FEATURE(feature) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) - #undef JSON_HEDLEY_GNUC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) - #undef JSON_HEDLEY_GCC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_EXTENSION) - #undef JSON_HEDLEY_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) -#else - #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) - #undef JSON_HEDLEY_GNUC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) - #undef JSON_HEDLEY_GCC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_WARNING) - #undef JSON_HEDLEY_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) -#else - #define JSON_HEDLEY_HAS_WARNING(warning) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) - #undef JSON_HEDLEY_GNUC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_WARNING) - #undef JSON_HEDLEY_GCC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - defined(__clang__) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) - #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_PRAGMA(value) __pragma(value) -#else - #define JSON_HEDLEY_PRAGMA(value) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) - #undef JSON_HEDLEY_DIAGNOSTIC_PUSH -#endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) - #undef JSON_HEDLEY_DIAGNOSTIC_POP -#endif -#if defined(__clang__) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) - #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) -#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_PUSH - #define JSON_HEDLEY_DIAGNOSTIC_POP -#endif - -/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") -# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") -# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# endif -#endif -#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x -#endif - -#if defined(JSON_HEDLEY_CONST_CAST) - #undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ - JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) - #undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) - #undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else - #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) - #undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ - ((T) (expr)) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("diag_suppress=Pe137") \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) -# endif -#else -# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif - -#if defined(JSON_HEDLEY_DEPRECATED) - #undef JSON_HEDLEY_DEPRECATED -#endif -#if defined(JSON_HEDLEY_DEPRECATED_FOR) - #undef JSON_HEDLEY_DEPRECATED_FOR -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) -#elif \ - (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) -#elif defined(__cplusplus) && (__cplusplus >= 201402L) - #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") -#else - #define JSON_HEDLEY_DEPRECATED(since) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) -#endif - -#if defined(JSON_HEDLEY_UNAVAILABLE) - #undef JSON_HEDLEY_UNAVAILABLE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) -#else - #define JSON_HEDLEY_UNAVAILABLE(available_since) -#endif - -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT -#endif -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) -#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#elif defined(_Check_return_) /* SAL */ - #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ -#else - #define JSON_HEDLEY_WARN_UNUSED_RESULT - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) -#endif - -#if defined(JSON_HEDLEY_SENTINEL) - #undef JSON_HEDLEY_SENTINEL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) -#else - #define JSON_HEDLEY_SENTINEL(position) -#endif - -#if defined(JSON_HEDLEY_NO_RETURN) - #undef JSON_HEDLEY_NO_RETURN -#endif -#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NO_RETURN __noreturn -#elif \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #define JSON_HEDLEY_NO_RETURN _Noreturn -#elif defined(__cplusplus) && (__cplusplus >= 201103L) - #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#else - #define JSON_HEDLEY_NO_RETURN -#endif - -#if defined(JSON_HEDLEY_NO_ESCAPE) - #undef JSON_HEDLEY_NO_ESCAPE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) - #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) -#else - #define JSON_HEDLEY_NO_ESCAPE -#endif - -#if defined(JSON_HEDLEY_UNREACHABLE) - #undef JSON_HEDLEY_UNREACHABLE -#endif -#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) - #undef JSON_HEDLEY_UNREACHABLE_RETURN -#endif -#if defined(JSON_HEDLEY_ASSUME) - #undef JSON_HEDLEY_ASSUME -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_ASSUME(expr) __assume(expr) -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) - #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #if defined(__cplusplus) - #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) - #else - #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) - #endif -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif defined(JSON_HEDLEY_ASSUME) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif -#if !defined(JSON_HEDLEY_ASSUME) - #if defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) - #else - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) - #endif -#endif -#if defined(JSON_HEDLEY_UNREACHABLE) - #if \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) - #else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() - #endif -#else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif - -JSON_HEDLEY_DIAGNOSTIC_PUSH -#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") - #pragma clang diagnostic ignored "-Wpedantic" -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) - #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif -#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) - #if defined(__clang__) - #pragma clang diagnostic ignored "-Wvariadic-macros" - #elif defined(JSON_HEDLEY_GCC_VERSION) - #pragma GCC diagnostic ignored "-Wvariadic-macros" - #endif -#endif -#if defined(JSON_HEDLEY_NON_NULL) - #undef JSON_HEDLEY_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#else - #define JSON_HEDLEY_NON_NULL(...) -#endif -JSON_HEDLEY_DIAGNOSTIC_POP - -#if defined(JSON_HEDLEY_PRINTF_FORMAT) - #undef JSON_HEDLEY_PRINTF_FORMAT -#endif -#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) -#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) -#else - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) -#endif - -#if defined(JSON_HEDLEY_CONSTEXPR) - #undef JSON_HEDLEY_CONSTEXPR -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) - #endif -#endif -#if !defined(JSON_HEDLEY_CONSTEXPR) - #define JSON_HEDLEY_CONSTEXPR -#endif - -#if defined(JSON_HEDLEY_PREDICT) - #undef JSON_HEDLEY_PREDICT -#endif -#if defined(JSON_HEDLEY_LIKELY) - #undef JSON_HEDLEY_LIKELY -#endif -#if defined(JSON_HEDLEY_UNLIKELY) - #undef JSON_HEDLEY_UNLIKELY -#endif -#if defined(JSON_HEDLEY_UNPREDICTABLE) - #undef JSON_HEDLEY_UNPREDICTABLE -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) - #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) -#elif \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ - })) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ - })) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) -# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) -#endif -#if !defined(JSON_HEDLEY_UNPREDICTABLE) - #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) -#endif - -#if defined(JSON_HEDLEY_MALLOC) - #undef JSON_HEDLEY_MALLOC -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_MALLOC __declspec(restrict) -#else - #define JSON_HEDLEY_MALLOC -#endif - -#if defined(JSON_HEDLEY_PURE) - #undef JSON_HEDLEY_PURE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ - ) -# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") -#else -# define JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_CONST) - #undef JSON_HEDLEY_CONST -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_CONST __attribute__((__const__)) -#elif \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_CONST _Pragma("no_side_effect") -#else - #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_RESTRICT) - #undef JSON_HEDLEY_RESTRICT -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT restrict -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - defined(__clang__) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RESTRICT __restrict -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT _Restrict -#else - #define JSON_HEDLEY_RESTRICT -#endif - -#if defined(JSON_HEDLEY_INLINE) - #undef JSON_HEDLEY_INLINE -#endif -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - (defined(__cplusplus) && (__cplusplus >= 199711L)) - #define JSON_HEDLEY_INLINE inline -#elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) - #define JSON_HEDLEY_INLINE __inline__ -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_INLINE __inline -#else - #define JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_ALWAYS_INLINE) - #undef JSON_HEDLEY_ALWAYS_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ - ) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") -#else -# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_NEVER_INLINE) - #undef JSON_HEDLEY_NEVER_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#else - #define JSON_HEDLEY_NEVER_INLINE -#endif - -#if defined(JSON_HEDLEY_PRIVATE) - #undef JSON_HEDLEY_PRIVATE -#endif -#if defined(JSON_HEDLEY_PUBLIC) - #undef JSON_HEDLEY_PUBLIC -#endif -#if defined(JSON_HEDLEY_IMPORT) - #undef JSON_HEDLEY_IMPORT -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC __declspec(dllexport) -# define JSON_HEDLEY_IMPORT __declspec(dllimport) -#else -# if \ - JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - ( \ - defined(__TI_EABI__) && \ - ( \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ - ) \ - ) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) -# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) -# else -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC -# endif -# define JSON_HEDLEY_IMPORT extern -#endif - -#if defined(JSON_HEDLEY_NO_THROW) - #undef JSON_HEDLEY_NO_THROW -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NO_THROW __declspec(nothrow) -#else - #define JSON_HEDLEY_NO_THROW -#endif - -#if defined(JSON_HEDLEY_FALL_THROUGH) - #undef JSON_HEDLEY_FALL_THROUGH -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) -#elif defined(__fallthrough) /* SAL */ - #define JSON_HEDLEY_FALL_THROUGH __fallthrough -#else - #define JSON_HEDLEY_FALL_THROUGH -#endif - -#if defined(JSON_HEDLEY_RETURNS_NON_NULL) - #undef JSON_HEDLEY_RETURNS_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) -#elif defined(_Ret_notnull_) /* SAL */ - #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ -#else - #define JSON_HEDLEY_RETURNS_NON_NULL -#endif - -#if defined(JSON_HEDLEY_ARRAY_PARAM) - #undef JSON_HEDLEY_ARRAY_PARAM -#endif -#if \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__STDC_NO_VLA__) && \ - !defined(__cplusplus) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_ARRAY_PARAM(name) (name) -#else - #define JSON_HEDLEY_ARRAY_PARAM(name) -#endif - -#if defined(JSON_HEDLEY_IS_CONSTANT) - #undef JSON_HEDLEY_IS_CONSTANT -#endif -#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) - #undef JSON_HEDLEY_REQUIRE_CONSTEXPR -#endif -/* JSON_HEDLEY_IS_CONSTEXPR_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #undef JSON_HEDLEY_IS_CONSTEXPR_ -#endif -#if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) -#endif -#if !defined(__cplusplus) -# if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) -#endif -# elif \ - ( \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ - !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION)) || \ - (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) -#endif -# elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - defined(JSON_HEDLEY_INTEL_VERSION) || \ - defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ - defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ - defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ - defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ - defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ - defined(__clang__) -# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ - sizeof(void) != \ - sizeof(*( \ - 1 ? \ - ((void*) ((expr) * 0L) ) : \ -((struct { char v[sizeof(void) * 2]; } *) 1) \ - ) \ - ) \ - ) -# endif -#endif -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) -#else - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) (0) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) -#endif - -#if defined(JSON_HEDLEY_BEGIN_C_DECLS) - #undef JSON_HEDLEY_BEGIN_C_DECLS -#endif -#if defined(JSON_HEDLEY_END_C_DECLS) - #undef JSON_HEDLEY_END_C_DECLS -#endif -#if defined(JSON_HEDLEY_C_DECL) - #undef JSON_HEDLEY_C_DECL -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { - #define JSON_HEDLEY_END_C_DECLS } - #define JSON_HEDLEY_C_DECL extern "C" -#else - #define JSON_HEDLEY_BEGIN_C_DECLS - #define JSON_HEDLEY_END_C_DECLS - #define JSON_HEDLEY_C_DECL -#endif - -#if defined(JSON_HEDLEY_STATIC_ASSERT) - #undef JSON_HEDLEY_STATIC_ASSERT -#endif -#if \ - !defined(__cplusplus) && ( \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ - (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - defined(_Static_assert) \ - ) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) -#elif \ - (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) -#else -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) -#endif - -#if defined(JSON_HEDLEY_NULL) - #undef JSON_HEDLEY_NULL -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) - #elif defined(NULL) - #define JSON_HEDLEY_NULL NULL - #else - #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) - #endif -#elif defined(NULL) - #define JSON_HEDLEY_NULL NULL -#else - #define JSON_HEDLEY_NULL ((void*) 0) -#endif - -#if defined(JSON_HEDLEY_MESSAGE) - #undef JSON_HEDLEY_MESSAGE -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_MESSAGE(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(message msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) -#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_WARNING) - #undef JSON_HEDLEY_WARNING -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_WARNING(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(clang warning msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) - #undef JSON_HEDLEY_REQUIRE -#endif -#if defined(JSON_HEDLEY_REQUIRE_MSG) - #undef JSON_HEDLEY_REQUIRE_MSG -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) -# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -# define JSON_HEDLEY_REQUIRE(expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), #expr, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), msg, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) -# endif -#else -# define JSON_HEDLEY_REQUIRE(expr) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) -#endif - -#if defined(JSON_HEDLEY_FLAGS) - #undef JSON_HEDLEY_FLAGS -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) - #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) -#else - #define JSON_HEDLEY_FLAGS -#endif - -#if defined(JSON_HEDLEY_FLAGS_CAST) - #undef JSON_HEDLEY_FLAGS_CAST -#endif -#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) -# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("warning(disable:188)") \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) -#endif - -#if defined(JSON_HEDLEY_EMPTY_BASES) - #undef JSON_HEDLEY_EMPTY_BASES -#endif -#if \ - (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) -#else - #define JSON_HEDLEY_EMPTY_BASES -#endif - -/* Remaining macros are deprecated. */ - -#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK -#endif -#if defined(__clang__) - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) -#else - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) - #undef JSON_HEDLEY_CLANG_HAS_BUILTIN -#endif -#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - -#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) - #undef JSON_HEDLEY_CLANG_HAS_FEATURE -#endif -#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - -#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) - #undef JSON_HEDLEY_CLANG_HAS_EXTENSION -#endif -#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) - -#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) - #undef JSON_HEDLEY_CLANG_HAS_WARNING -#endif -#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - -#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ - - -// This file contains all internal macro definitions (except those affecting ABI) -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// #include - - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 -#endif - -#ifdef __has_include - #if __has_include() - #include - #endif -#endif - -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #ifdef JSON_HAS_CPP_17 - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif - - // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ - #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__clang_major__) && __clang_major__ < 7 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1914 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before iOS 13 - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before macOS Catalina - #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - #endif -#endif - -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_FILESYSTEM - #define JSON_HAS_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_THREE_WAY_COMPARISON - #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ - && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L - #define JSON_HAS_THREE_WAY_COMPARISON 1 - #else - #define JSON_HAS_THREE_WAY_COMPARISON 0 - #endif -#endif - -#ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error - #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 - #define JSON_HAS_RANGES 0 - #elif defined(__cpp_lib_ranges) - #define JSON_HAS_RANGES 1 - #else - #define JSON_HAS_RANGES 0 - #endif -#endif - -#ifdef JSON_HAS_CPP_17 - #define JSON_INLINE_VARIABLE inline -#else - #define JSON_INLINE_VARIABLE -#endif - -#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) - #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else - #define JSON_NO_UNIQUE_ADDRESS -#endif - -// disable documentation warnings on clang -#if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdocumentation" - #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -// allow disabling exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) - #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) -#else - #include - #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) - #undef JSON_THROW - #define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) - #undef JSON_TRY - #define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) - #undef JSON_CATCH - #define JSON_CATCH JSON_CATCH_USER - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// allow overriding assert -#if !defined(JSON_ASSERT) - #include // assert - #define JSON_ASSERT(x) assert(x) -#endif - -// allow to access some private functions (needed by the test suite) -#if defined(JSON_TESTS_PRIVATE) - #define JSON_PRIVATE_UNLESS_TESTED public -#else - #define JSON_PRIVATE_UNLESS_TESTED private -#endif - -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template class ObjectType, \ - template class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template class AllocatorType, \ - template class JSONSerializer, \ - class BinaryType> - -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json - -// Macros to simplify conversion from/to types - -#define NLOHMANN_JSON_EXPAND( x ) x -#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME -#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ - NLOHMANN_JSON_PASTE64, \ - NLOHMANN_JSON_PASTE63, \ - NLOHMANN_JSON_PASTE62, \ - NLOHMANN_JSON_PASTE61, \ - NLOHMANN_JSON_PASTE60, \ - NLOHMANN_JSON_PASTE59, \ - NLOHMANN_JSON_PASTE58, \ - NLOHMANN_JSON_PASTE57, \ - NLOHMANN_JSON_PASTE56, \ - NLOHMANN_JSON_PASTE55, \ - NLOHMANN_JSON_PASTE54, \ - NLOHMANN_JSON_PASTE53, \ - NLOHMANN_JSON_PASTE52, \ - NLOHMANN_JSON_PASTE51, \ - NLOHMANN_JSON_PASTE50, \ - NLOHMANN_JSON_PASTE49, \ - NLOHMANN_JSON_PASTE48, \ - NLOHMANN_JSON_PASTE47, \ - NLOHMANN_JSON_PASTE46, \ - NLOHMANN_JSON_PASTE45, \ - NLOHMANN_JSON_PASTE44, \ - NLOHMANN_JSON_PASTE43, \ - NLOHMANN_JSON_PASTE42, \ - NLOHMANN_JSON_PASTE41, \ - NLOHMANN_JSON_PASTE40, \ - NLOHMANN_JSON_PASTE39, \ - NLOHMANN_JSON_PASTE38, \ - NLOHMANN_JSON_PASTE37, \ - NLOHMANN_JSON_PASTE36, \ - NLOHMANN_JSON_PASTE35, \ - NLOHMANN_JSON_PASTE34, \ - NLOHMANN_JSON_PASTE33, \ - NLOHMANN_JSON_PASTE32, \ - NLOHMANN_JSON_PASTE31, \ - NLOHMANN_JSON_PASTE30, \ - NLOHMANN_JSON_PASTE29, \ - NLOHMANN_JSON_PASTE28, \ - NLOHMANN_JSON_PASTE27, \ - NLOHMANN_JSON_PASTE26, \ - NLOHMANN_JSON_PASTE25, \ - NLOHMANN_JSON_PASTE24, \ - NLOHMANN_JSON_PASTE23, \ - NLOHMANN_JSON_PASTE22, \ - NLOHMANN_JSON_PASTE21, \ - NLOHMANN_JSON_PASTE20, \ - NLOHMANN_JSON_PASTE19, \ - NLOHMANN_JSON_PASTE18, \ - NLOHMANN_JSON_PASTE17, \ - NLOHMANN_JSON_PASTE16, \ - NLOHMANN_JSON_PASTE15, \ - NLOHMANN_JSON_PASTE14, \ - NLOHMANN_JSON_PASTE13, \ - NLOHMANN_JSON_PASTE12, \ - NLOHMANN_JSON_PASTE11, \ - NLOHMANN_JSON_PASTE10, \ - NLOHMANN_JSON_PASTE9, \ - NLOHMANN_JSON_PASTE8, \ - NLOHMANN_JSON_PASTE7, \ - NLOHMANN_JSON_PASTE6, \ - NLOHMANN_JSON_PASTE5, \ - NLOHMANN_JSON_PASTE4, \ - NLOHMANN_JSON_PASTE3, \ - NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) -#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) -#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) -#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) -#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) -#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) -#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) -#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) -#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) -#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) -#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) -#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) -#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) -#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) -#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) -#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) -#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) -#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) -#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) -#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) -#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) -#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) -#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) -#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) -#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) -#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) -#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) -#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) -#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) -#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) -#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) -#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) -#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) -#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) -#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) -#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) -#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) -#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) -#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) -#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) -#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) -#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) -#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) -#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) -#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) -#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) -#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) -#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) -#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) -#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) -#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) -#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) -#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) -#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) -#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) -#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) -#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) -#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) -#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) -#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) -#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) -#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) - -#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE -@since version 3.9.0 -*/ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE -@since version 3.9.0 -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - - -// inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): -// using std::begin; begin(x); -// -// it allows using the detected idiom to retrieve the return type -// of such an expression -#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ - namespace detail { \ - using std::std_name; \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - } \ - \ - namespace detail2 { \ - struct std_name##_tag \ - { \ - }; \ - \ - template \ - std_name##_tag std_name(T&&...); \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - \ - template \ - struct would_call_std_##std_name \ - { \ - static constexpr auto const value = ::nlohmann::detail:: \ - is_detected_exact::value; \ - }; \ - } /* namespace detail2 */ \ - \ - template \ - struct would_call_std_##std_name : detail2::would_call_std_##std_name \ - { \ - } - -#ifndef JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_USE_IMPLICIT_CONVERSIONS 1 -#endif - -#if JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_EXPLICIT -#else - #define JSON_EXPLICIT explicit -#endif - -#ifndef JSON_DISABLE_ENUM_SERIALIZATION - #define JSON_DISABLE_ENUM_SERIALIZATION 0 -#endif - -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif - -#if JSON_HAS_THREE_WAY_COMPARISON - #include // partial_ordering -#endif - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -#if JSON_HAS_THREE_WAY_COMPARISON - inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* -#else - inline bool operator<(const value_t lhs, const value_t rhs) noexcept -#endif -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); -#if JSON_HAS_THREE_WAY_COMPARISON - if (l_index < order.size() && r_index < order.size()) - { - return order[l_index] <=> order[r_index]; // *NOPAD* - } - return std::partial_ordering::unordered; -#else - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -#endif -} - -// GCC selects the built-in operator< over an operator rewritten from -// a user-defined spaceship operator -// Clang, MSVC, and ICC select the rewritten candidate -// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) -#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - return std::is_lt(lhs <=> rhs); // *NOPAD* -} -#endif - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/*! -@brief replace all occurrences of a substring by another string - -@param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t -@param[in] f the substring to replace with @a t -@param[in] t the string to replace @a f - -@pre The search string @a f must not be empty. **This precondition is -enforced with an assertion.** - -@since version 2.0.0 -*/ -template -inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) -{ - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != StringType::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} -} - -/*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string - * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. - */ -template -inline StringType escape(StringType s) -{ - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); - return s; -} - -/*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * @return unescaped string - * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. - */ -template -static void unescape(StringType& s) -{ - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // size_t - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-FileCopyrightText: 2018 The Abseil Authors -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; - -#else - -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; - -// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h -// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. - -//// START OF CODE FROM GOOGLE ABSEIL - -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence()); -// } -template -struct integer_sequence -{ - using value_type = T; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; - -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template -using index_sequence = integer_sequence; - -namespace utility_internal -{ - -template -struct Extend; - -// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. -template -struct Extend, SeqSize, 0> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; -}; - -template -struct Extend, SeqSize, 1> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; -}; - -// Recursion helper for 'make_integer_sequence'. -// 'Gen::type' is an alias for 'integer_sequence'. -template -struct Gen -{ - using type = - typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; -}; - -template -struct Gen -{ - using type = integer_sequence; -}; - -} // namespace utility_internal - -// Compile-time sequences of integers - -// make_integer_sequence -// -// This template alias is equivalent to -// `integer_sequence`, and is designed to be a drop-in -// replacement for C++14's `std::make_integer_sequence`. -template -using make_integer_sequence = typename utility_internal::Gen::type; - -// make_index_sequence -// -// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, -// and is designed to be a drop-in replacement for C++14's -// `std::make_index_sequence`. -template -using make_index_sequence = make_integer_sequence; - -// index_sequence_for -// -// Converts a typename pack into an index sequence of the same length, and -// is designed to be a drop-in replacement for C++14's -// `std::index_sequence_for()` -template -using index_sequence_for = make_index_sequence; - -//// END OF CODE FROM GOOGLE ABSEIL - -#endif - -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; - -// taken from ranges-v3 -template -struct static_const -{ - static JSON_INLINE_VARIABLE constexpr T value{}; -}; - -#ifndef JSON_HAS_CPP_17 - template - constexpr T static_const::value; -#endif - -template -inline constexpr std::array make_array(Args&& ... args) -{ - return std::array {{static_cast(std::forward(args))...}}; -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval -#include // tuple - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // random_access_iterator_tag - -// #include - -// #include - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -struct iterator_types {}; - -template -struct iterator_types < - It, - void_t> -{ - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; -}; - -// This is required as some compilers implement std::iterator_traits in a way that -// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. -template -struct iterator_traits -{ -}; - -template -struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types -{ -}; - -template -struct iterator_traits::value>> -{ - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); - -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); - -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - #include // int64_t, uint64_t - #include // map - #include // allocator - #include // string - #include // vector - - // #include - - - /*! - @brief namespace for Niels Lohmann - @see https://github.com/nlohmann - @since version 1.0.0 - */ - NLOHMANN_JSON_NAMESPACE_BEGIN - - /*! - @brief default JSONSerializer template argument - - This serializer ignores the template arguments and uses ADL - ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) - for serialization. - */ - template - struct adl_serializer; - - /// a class to store JSON values - /// @sa https://json.nlohmann.me/api/basic_json/ - template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector> - class basic_json; - - /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document - /// @sa https://json.nlohmann.me/api/json_pointer/ - template - class json_pointer; - - /*! - @brief default specialization - @sa https://json.nlohmann.me/api/json/ - */ - using json = basic_json<>; - - /// @brief a minimal map-like container that preserves insertion order - /// @sa https://json.nlohmann.me/api/ordered_map/ - template - struct ordered_map; - - /// @brief specialization that maintains the insertion order of object keys - /// @sa https://json.nlohmann.me/api/ordered_json/ - using ordered_json = basic_json; - - NLOHMANN_JSON_NAMESPACE_END - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - -NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ - -///////////// -// helpers // -///////////// - -// Note to maintainers: -// -// Every trait in this file expects a non CV-qualified type. -// The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) -// -// In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) - -template struct is_basic_json : std::false_type {}; - -NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; - -// used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t -// false_type otherwise -template -struct is_basic_json_context : - std::integral_constant < bool, - is_basic_json::type>::type>::value - || std::is_same::value > -{}; - -////////////////////// -// json_ref helpers // -////////////////////// - -template -class json_ref; - -template -struct is_json_ref : std::false_type {}; - -template -struct is_json_ref> : std::true_type {}; - -////////////////////////// -// aliases for detected // -////////////////////////// - -template -using mapped_type_t = typename T::mapped_type; - -template -using key_type_t = typename T::key_type; - -template -using value_type_t = typename T::value_type; - -template -using difference_type_t = typename T::difference_type; - -template -using pointer_t = typename T::pointer; - -template -using reference_t = typename T::reference; - -template -using iterator_category_t = typename T::iterator_category; - -template -using to_json_function = decltype(T::to_json(std::declval()...)); - -template -using from_json_function = decltype(T::from_json(std::declval()...)); - -template -using get_template_function = decltype(std::declval().template get()); - -// trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json : std::false_type {}; - -// trait checking if j.get is valid -// use this trait instead of std::is_constructible or std::is_convertible, -// both rely on, or make use of implicit conversions, and thus fail when T -// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template -struct is_getable -{ - static constexpr bool value = is_detected::value; -}; - -template -struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if JSONSerializer::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template -struct has_non_default_from_json : std::false_type {}; - -template -struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if BasicJsonType::json_serializer::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template -struct has_to_json : std::false_type {}; - -template -struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -template -using detect_key_compare = typename T::key_compare; - -template -struct has_key_compare : std::integral_constant::value> {}; - -// obtains the actual object key comparator -template -struct actual_object_comparator -{ - using object_t = typename BasicJsonType::object_t; - using object_comparator_t = typename BasicJsonType::default_object_comparator_t; - using type = typename std::conditional < has_key_compare::value, - typename object_t::key_compare, object_comparator_t>::type; -}; - -template -using actual_object_comparator_t = typename actual_object_comparator::type; - -/////////////////// -// is_ functions // -/////////////////// - -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B { }; -template -struct conjunction -: std::conditional(B::value), conjunction, B>::type {}; - -// https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; - -// Reimplementation of is_constructible and is_default_constructible, due to them being broken for -// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. -template -struct is_default_constructible : std::is_default_constructible {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - - -template -struct is_constructible : std::is_constructible {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - - -template -struct is_iterator_traits : std::false_type {}; - -template -struct is_iterator_traits> -{ - private: - using traits = iterator_traits; - - public: - static constexpr auto value = - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value; -}; - -template -struct is_range -{ - private: - using t_ref = typename std::add_lvalue_reference::type; - - using iterator = detected_t; - using sentinel = detected_t; - - // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator - // and https://en.cppreference.com/w/cpp/iterator/sentinel_for - // but reimplementing these would be too much work, as a lot of other concepts are used underneath - static constexpr auto is_iterator_begin = - is_iterator_traits>::value; - - public: - static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; -}; - -template -using iterator_t = enable_if_t::value, result_of_begin())>>; - -template -using range_value_t = value_type_t>>; - -// The following implementation of is_complete_type is taken from -// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. - -template -struct is_complete_type : std::false_type {}; - -template -struct is_complete_type : std::true_type {}; - -template -struct is_compatible_object_type_impl : std::false_type {}; - -template -struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - is_constructible::value && - is_constructible::value; -}; - -template -struct is_compatible_object_type - : is_compatible_object_type_impl {}; - -template -struct is_constructible_object_type_impl : std::false_type {}; - -template -struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = - (is_default_constructible::value && - (std::is_move_assignable::value || - std::is_copy_assignable::value) && - (is_constructible::value && - std::is_same < - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) || - (has_from_json::value || - has_non_default_from_json < - BasicJsonType, - typename ConstructibleObjectType::mapped_type >::value); -}; - -template -struct is_constructible_object_type - : is_constructible_object_type_impl {}; - -template -struct is_compatible_string_type -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_constructible_string_type -{ - // launder type through decltype() to fix compilation failure on ICPC -#ifdef __INTEL_COMPILER - using laundered_type = decltype(std::declval()); -#else - using laundered_type = ConstructibleStringType; -#endif - - static constexpr auto value = - conjunction < - is_constructible, - is_detected_exact>::value; -}; - -template -struct is_compatible_array_type_impl : std::false_type {}; - -template -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < - is_detected::value&& - is_iterator_traits>>::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value >> -{ - static constexpr bool value = - is_constructible>::value; -}; - -template -struct is_compatible_array_type - : is_compatible_array_type_impl {}; - -template -struct is_constructible_array_type_impl : std::false_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - !is_compatible_string_type::value&& - is_default_constructible::value&& -(std::is_move_assignable::value || - std::is_copy_assignable::value)&& -is_detected::value&& -is_iterator_traits>>::value&& -is_detected::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 -!std::is_same>::value&& - is_complete_type < - detected_t>::value >> -{ - using value_type = range_value_t; - - static constexpr bool value = - std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, - value_type >::value; -}; - -template -struct is_constructible_array_type - : is_constructible_array_type_impl {}; - -template -struct is_compatible_integer_type_impl : std::false_type {}; - -template -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template -struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - -template -struct is_compatible_type_impl: std::false_type {}; - -template -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> -{ - static constexpr bool value = - has_to_json::value; -}; - -template -struct is_compatible_type - : is_compatible_type_impl {}; - -template -struct is_constructible_tuple : std::false_type {}; - -template -struct is_constructible_tuple> : conjunction...> {}; - -template -struct is_json_iterator_of : std::false_type {}; - -template -struct is_json_iterator_of : std::true_type {}; - -template -struct is_json_iterator_of : std::true_type -{}; - -// checks if a given type T is a template specialization of Primary -template