Compare commits

...

10 Commits

Author SHA1 Message Date
Blake Mizerany
2bed62926e types/model: remove Digest (for now) (#3970)
The Digest type needs more thought and is not necessary at the moment.
2024-04-26 21:14:28 -07:00
Jeffrey Morgan
aad8d128a0 also look at cwd as a root for windows runners (#3959) 2024-04-26 19:14:08 -04:00
Daniel Hiltgen
ec1acbb867 Merge pull request #3968 from dhiltgen/win_generate
Fine grain control over windows generate steps
2024-04-26 16:03:38 -07:00
Daniel Hiltgen
e4859c4563 Fine grain control over windows generate steps
This will speed up CI which already tries to only build static for unit tests
2024-04-26 15:49:46 -07:00
Nataly Merezhuk
8e30eb26bd Updates the setup command to use llama3. (#3962) 2024-04-26 18:41:01 -04:00
Daniel Hiltgen
0b5c589ca2 Merge pull request #3966 from dhiltgen/bump
Fix target in gen_windows.ps1
2024-04-26 15:36:53 -07:00
Michael Yang
65fadddc85 Merge pull request #3964 from ollama/mxyng/weights
fix gemma, command-r layer weights
2024-04-26 15:23:33 -07:00
Daniel Hiltgen
ed5fb088c4 Fix target in gen_windows.ps1 2024-04-26 15:10:42 -07:00
Michael Yang
f81f308118 fix gemma, command-r layer weights 2024-04-26 15:00:55 -07:00
Blake Mizerany
b1390a7b37 types/model: export ParseNameBare and Merge (#3957)
These are useful outside this package.
2024-04-26 14:58:07 -07:00
6 changed files with 59 additions and 165 deletions

View File

@@ -129,7 +129,7 @@ SetupAppRunningError=Another Ollama installer is running.%n%nPlease cancel or fi
;FinishedHeadingLabel=Run your first model
;FinishedLabel=%nRun this command in a PowerShell or cmd terminal.%n%n%n ollama run llama2
;FinishedLabel=%nRun this command in a PowerShell or cmd terminal.%n%n%n ollama run llama3
;ClickFinish=%n
[Registry]

View File

@@ -32,9 +32,25 @@ func PayloadsDir() (string, error) {
slog.Error("failed to lookup executable path", "error", err)
return "", err
}
cwd, err := os.Getwd()
if err != nil {
slog.Error("failed to lookup working directory", "error", err)
return "", err
}
var paths []string
for _, root := range []string{appExe, cwd} {
paths = append(paths,
filepath.Join(root),
filepath.Join(root, "windows-"+runtime.GOARCH),
filepath.Join(root, "dist", "windows-"+runtime.GOARCH),
)
}
// Try a few variations to improve developer experience when building from source in the local tree
for _, d := range []string{".", "windows-" + runtime.GOARCH, "dist\\windows-" + runtime.GOARCH} {
candidate := filepath.Join(filepath.Dir(appExe), d, "ollama_runners")
for _, p := range paths {
candidate := filepath.Join(p, "ollama_runners")
_, err := os.Stat(candidate)
if err == nil {
runnersDir = candidate

View File

@@ -26,7 +26,6 @@ function amdGPUs {
$GPU_LIST -join ';'
}
$script:cmakeTargets = @("ollama_llama_server")
function init_vars {
if (!$script:SRC_DIR) {
@@ -35,6 +34,9 @@ function init_vars {
if (!$script:llamacppDir) {
$script:llamacppDir = "../llama.cpp"
}
if (!$script:cmakeTargets) {
$script:cmakeTargets = @("ollama_llama_server")
}
$script:cmakeDefs = @(
"-DBUILD_SHARED_LIBS=on",
"-DLLAMA_NATIVE=off"
@@ -42,6 +44,7 @@ function init_vars {
$script:commonCpuDefs = @("-DCMAKE_POSITION_INDEPENDENT_CODE=on")
$script:ARCH = "amd64" # arm not yet supported.
$script:DIST_BASE = "${script:SRC_DIR}\dist\windows-${script:ARCH}\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")
$script:config = "RelWithDebInfo"
@@ -179,7 +182,7 @@ function cleanup {
function build_static() {
if ($null -eq ${env:OLLAMA_SKIP_CPU_GENERATE}) {
if ((-not "${env:OLLAMA_SKIP_STATIC_GENERATE}") -and ((-not "${env:OLLAMA_CPU_TARGET}") -or ("${env:OLLAMA_CPU_TARGET}" -eq "static"))) {
# GCC build for direct linking into the Go binary
init_vars
# cmake will silently fallback to msvc compilers if mingw isn't in the path, so detect and fail fast
@@ -188,6 +191,7 @@ function build_static() {
# error action ensures we exit on failure
get-command gcc
get-command mingw32-make
$oldTargets = $script:cmakeTargets
$script:cmakeTargets = @("llama", "ggml")
$script:cmakeDefs = @(
"-G", "MinGW Makefiles"
@@ -203,13 +207,14 @@ function build_static() {
$script:buildDir="../build/windows/${script:ARCH}_static"
write-host "Building static library"
build
$script:cmakeTargets = $oldTargets
} else {
write-host "Skipping CPU generation step as requested"
}
}
function build_cpu() {
if ($null -eq ${env:OLLAMA_SKIP_CPU_GENERATE}) {
if ((-not "${env:OLLAMA_SKIP_CPU_GENERATE}" ) -and ((-not "${env:OLLAMA_CPU_TARGET}") -or ("${env:OLLAMA_CPU_TARGET}" -eq "cpu"))) {
# remaining llama.cpp builds use MSVC
init_vars
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=off", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
@@ -225,7 +230,7 @@ function build_cpu() {
}
function build_cpu_avx() {
if ($null -eq ${env:OLLAMA_SKIP_CPU_GENERATE}) {
if ((-not "${env:OLLAMA_SKIP_CPU_GENERATE}" ) -and ((-not "${env:OLLAMA_CPU_TARGET}") -or ("${env:OLLAMA_CPU_TARGET}" -eq "cpu_avx"))) {
init_vars
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
$script:buildDir="../build/windows/${script:ARCH}/cpu_avx"
@@ -235,12 +240,12 @@ function build_cpu_avx() {
sign
install
} else {
write-host "Skipping CPU generation step as requested"
write-host "Skipping CPU AVX generation step as requested"
}
}
function build_cpu_avx2() {
if ($null -eq ${env:OLLAMA_SKIP_CPU_GENERATE}) {
if ((-not "${env:OLLAMA_SKIP_CPU_GENERATE}" ) -and ((-not "${env:OLLAMA_CPU_TARGET}") -or ("${env:OLLAMA_CPU_TARGET}" -eq "cpu_avx2"))) {
init_vars
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=on", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=on", "-DLLAMA_F16C=on") + $script:cmakeDefs
$script:buildDir="../build/windows/${script:ARCH}/cpu_avx2"
@@ -250,12 +255,12 @@ function build_cpu_avx2() {
sign
install
} else {
write-host "Skipping CPU generation step as requested"
write-host "Skipping CPU AVX2 generation step as requested"
}
}
function build_cuda() {
if ($null -ne $script:CUDA_LIB_DIR) {
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
@@ -279,11 +284,13 @@ function build_cuda() {
cp "${script:CUDA_LIB_DIR}\cudart64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\"
cp "${script:CUDA_LIB_DIR}\cublas64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\"
cp "${script:CUDA_LIB_DIR}\cublasLt64_*.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\"
} else {
write-host "Skipping CUDA generation step"
}
}
function build_rocm() {
if ($null -ne $env:HIP_PATH) {
if ((-not "${env:OLLAMA_SKIP_ROCM_GENERATE}") -and ("${env:HIP_PATH}")) {
$script:ROCM_VERSION=(get-item $env:HIP_PATH).Basename
if ($null -ne $script:ROCM_VERSION) {
$script:ROCM_VARIANT="_v"+$script:ROCM_VERSION
@@ -332,6 +339,8 @@ function build_rocm() {
cp "${env:HIP_PATH}\bin\rocblas.dll" "${script:SRC_DIR}\dist\windows-${script:ARCH}\rocm\"
# 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\"
} else {
write-host "Skipping ROCm generation step"
}
}

View File

@@ -102,10 +102,14 @@ func EstimateGPULayers(gpus []gpu.GpuInfo, ggml *GGML, projectors []string, opts
layers := ggml.Tensors().Layers()
var memoryLayerOutput uint64
for k, v := range layers {
if k == "output" || k == "output_norm" {
memoryLayerOutput += v.size()
}
if layer, ok := layers["output_norm"]; ok {
memoryLayerOutput += layer.size()
}
if layer, ok := layers["output"]; ok {
memoryLayerOutput += layer.size()
} else if layer, ok := layers["token_embd"]; ok {
memoryLayerOutput += layer.size()
}
if gpus[0].Library == "metal" && opts.UseMMap {

View File

@@ -4,7 +4,6 @@ package model
import (
"cmp"
"encoding/hex"
"errors"
"fmt"
"log/slog"
@@ -132,11 +131,12 @@ type Name struct {
// field values are left in an undefined state. Use [Name.IsValid] to check
// if the name is valid.
func ParseName(s string) Name {
return merge(parseName(s), DefaultName())
return Merge(ParseNameBare(s), DefaultName())
}
// parseName is the same as [ParseName] without a merge.
func parseName(s string) Name {
// ParseNameBare parses s as a name string and returns a Name. No merge with
// [DefaultName] is performed.
func ParseNameBare(s string) Name {
var n Name
var promised bool
@@ -161,20 +161,15 @@ func parseName(s string) Name {
return n
}
// merge merges the host, namespace, and tag parts of the two names,
// Merge merges the host, namespace, and tag parts of the two names,
// preferring the non-empty parts of a.
func merge(a, b Name) Name {
func Merge(a, b Name) Name {
a.Host = cmp.Or(a.Host, b.Host)
a.Namespace = cmp.Or(a.Namespace, b.Namespace)
a.Tag = cmp.Or(a.Tag, b.Tag)
return a
}
// Digest returns the result of [ParseDigest] with the RawDigest field.
func (n Name) Digest() Digest {
return ParseDigest(n.RawDigest)
}
// String returns the name string, in the format that [ParseNameNoDefaults]
// accepts as valid, if [Name.IsValid] reports true; otherwise the empty
// string is returned.
@@ -203,7 +198,7 @@ func (n Name) String() string {
// IsValid reports whether all parts of the name are present and valid. The
// digest is a special case, and is checked for validity only if present.
func (n Name) IsValid() bool {
if n.RawDigest != "" && !ParseDigest(n.RawDigest).IsValid() {
if n.RawDigest != "" && !isValidPart(kindDigest, n.RawDigest) {
return false
}
return n.IsFullyQualified()
@@ -281,7 +276,7 @@ func isValidPart(kind partKind, s string) bool {
return false
}
case ':':
if kind != kindHost {
if kind != kindHost && kind != kindDigest {
return false
}
default:
@@ -316,75 +311,3 @@ func cutPromised(s, sep string) (before, after string, ok bool) {
}
return cmp.Or(before, MissingPart), cmp.Or(after, MissingPart), true
}
type DigestType int
const (
DigestTypeInvalid DigestType = iota
DigestTypeSHA256
)
func (t DigestType) String() string {
if t == DigestTypeSHA256 {
return "sha256"
}
return "unknown"
}
// Digest represents a type and hash of a digest. It is comparable and can
// be used as a map key.
type Digest struct {
Type DigestType
Hash [32]byte
}
// ParseDigest parses a digest string into a Digest struct. It accepts both
// the forms:
//
// sha256:deadbeef
// sha256-deadbeef
//
// The hash part must be exactly 64 characters long.
//
// The form "type:hash" does not round trip through [Digest.String].
func ParseDigest(s string) Digest {
typ, hash, ok := cutLast(s, ":")
if !ok {
typ, hash, ok = cutLast(s, "-")
if !ok {
return Digest{}
}
}
if typ != "sha256" {
return Digest{}
}
var d Digest
n, err := hex.Decode(d.Hash[:], []byte(hash))
if err != nil || n != 32 {
return Digest{}
}
return Digest{Type: DigestTypeSHA256, Hash: d.Hash}
}
// IsValid returns true if the digest has a valid Type and Hash.
func (d Digest) IsValid() bool {
if d.Type != DigestTypeSHA256 {
return false
}
return d.Hash != [32]byte{}
}
// String returns the digest as a string in the form "type-hash". The hash
// is encoded as a hex string.
func (d Digest) String() string {
var b strings.Builder
b.WriteString(d.Type.String())
b.WriteByte('-')
b.WriteString(hex.EncodeToString(d.Hash[:]))
return b.String()
}
// LogValue returns a slog.Value that represents the digest as a string.
func (d Digest) LogValue() slog.Value {
return slog.StringValue(d.String())
}

View File

@@ -2,7 +2,6 @@ package model
import (
"reflect"
"strings"
"testing"
)
@@ -82,10 +81,10 @@ func TestParseNameParts(t *testing.T) {
wantValidDigest: false,
},
{
in: "model@sha256:" + validSHA256Hex,
in: "model@sha256:123",
want: Name{
Model: "model",
RawDigest: "sha256:" + validSHA256Hex,
RawDigest: "sha256:123",
},
wantValidDigest: true,
},
@@ -93,13 +92,10 @@ func TestParseNameParts(t *testing.T) {
for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) {
got := parseName(tt.in)
got := ParseNameBare(tt.in)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseName(%q) = %v; want %v", tt.in, got, tt.want)
}
if got.Digest().IsValid() != tt.wantValidDigest {
t.Errorf("parseName(%q).Digest().IsValid() = %v; want %v", tt.in, got.Digest().IsValid(), tt.wantValidDigest)
}
})
}
}
@@ -166,7 +162,7 @@ func TestNameparseNameDefault(t *testing.T) {
func TestNameIsValid(t *testing.T) {
var numStringTests int
for s, want := range testCases {
n := parseName(s)
n := ParseNameBare(s)
t.Logf("n: %#v", n)
got := n.IsValid()
if got != want {
@@ -175,7 +171,7 @@ func TestNameIsValid(t *testing.T) {
// Test roundtrip with String
if got {
got := parseName(s).String()
got := ParseNameBare(s).String()
if got != s {
t.Errorf("parseName(%q).String() = %q; want %q", s, got, s)
}
@@ -221,7 +217,7 @@ func FuzzName(f *testing.F) {
f.Add(s)
}
f.Fuzz(func(t *testing.T, s string) {
n := parseName(s)
n := ParseNameBare(s)
if n.IsValid() {
parts := [...]string{n.Host, n.Namespace, n.Model, n.Tag, n.RawDigest}
for _, part := range parts {
@@ -239,57 +235,3 @@ func FuzzName(f *testing.F) {
})
}
const validSHA256Hex = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
func TestParseDigest(t *testing.T) {
cases := map[string]bool{
"sha256-1000000000000000000000000000000000000000000000000000000000000000": true,
"sha256:1000000000000000000000000000000000000000000000000000000000000000": true,
"sha256:0000000000000000000000000000000000000000000000000000000000000000": false,
"sha256:" + validSHA256Hex: true,
"sha256-" + validSHA256Hex: true,
"": false,
"sha134:" + validSHA256Hex: false,
"sha256:" + validSHA256Hex + "x": false,
"sha256:x" + validSHA256Hex: false,
"sha256-" + validSHA256Hex + "x": false,
"sha256-x": false,
}
for s, want := range cases {
t.Run(s, func(t *testing.T) {
d := ParseDigest(s)
if d.IsValid() != want {
t.Errorf("ParseDigest(%q).IsValid() = %v; want %v", s, d.IsValid(), want)
}
norm := strings.ReplaceAll(s, ":", "-")
if d.IsValid() && d.String() != norm {
t.Errorf("ParseDigest(%q).String() = %q; want %q", s, d.String(), norm)
}
})
}
}
func TestDigestString(t *testing.T) {
cases := []struct {
in string
want string
}{
{in: "sha256:" + validSHA256Hex, want: "sha256-" + validSHA256Hex},
{in: "sha256-" + validSHA256Hex, want: "sha256-" + validSHA256Hex},
{in: "", want: "unknown-0000000000000000000000000000000000000000000000000000000000000000"},
{in: "blah-100000000000000000000000000000000000000000000000000000000000000", want: "unknown-0000000000000000000000000000000000000000000000000000000000000000"},
}
for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) {
d := ParseDigest(tt.in)
if d.String() != tt.want {
t.Errorf("ParseDigest(%q).String() = %q; want %q", tt.in, d.String(), tt.want)
}
})
}
}