mirror of
https://github.com/ollama/ollama.git
synced 2026-01-29 01:33:06 -05:00
Compare commits
10 Commits
jmorganca/
...
jmorganca/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bed62926e | ||
|
|
aad8d128a0 | ||
|
|
ec1acbb867 | ||
|
|
e4859c4563 | ||
|
|
8e30eb26bd | ||
|
|
0b5c589ca2 | ||
|
|
65fadddc85 | ||
|
|
ed5fb088c4 | ||
|
|
f81f308118 | ||
|
|
b1390a7b37 |
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user