174 lines
4.3 KiB
Go
174 lines
4.3 KiB
Go
package llm
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/ollama/ollama/ml"
|
|
)
|
|
|
|
type llamaCppBinarySearch struct {
|
|
libOllamaPath string
|
|
executable string
|
|
workingDir string
|
|
goos string
|
|
goarch string
|
|
}
|
|
|
|
func defaultLlamaCppBinarySearch() llamaCppBinarySearch {
|
|
executable, _ := os.Executable()
|
|
if executable != "" {
|
|
if eval, err := filepath.EvalSymlinks(executable); err == nil {
|
|
executable = eval
|
|
}
|
|
}
|
|
|
|
workingDir, _ := os.Getwd()
|
|
return llamaCppBinarySearch{
|
|
libOllamaPath: ml.LibOllamaPath,
|
|
executable: executable,
|
|
workingDir: workingDir,
|
|
goos: runtime.GOOS,
|
|
goarch: runtime.GOARCH,
|
|
}
|
|
}
|
|
|
|
// FindLlamaCppBinary locates a llama.cpp helper binary in installed and local
|
|
// development layouts.
|
|
func FindLlamaCppBinary(name string) (string, error) {
|
|
path, candidates, err := findLlamaCppBinary(name, defaultLlamaCppBinarySearch())
|
|
if err != nil {
|
|
return "", fmt.Errorf("%s binary not found (checked: %s)", name, strings.Join(candidates, ", "))
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func findLlamaCppBinary(name string, search llamaCppBinarySearch) (string, []string, error) {
|
|
candidates := llamaCppBinaryCandidates(name, search)
|
|
for _, path := range candidates {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return path, candidates, nil
|
|
}
|
|
}
|
|
|
|
return "", candidates, os.ErrNotExist
|
|
}
|
|
|
|
func llamaCppBinaryCandidates(name string, search llamaCppBinarySearch) []string {
|
|
goos := search.goos
|
|
if goos == "" {
|
|
goos = runtime.GOOS
|
|
}
|
|
goarch := search.goarch
|
|
if goarch == "" {
|
|
goarch = runtime.GOARCH
|
|
}
|
|
|
|
suffix := llamaCppBinaryName(name, goos)
|
|
seen := map[string]bool{}
|
|
var candidates []string
|
|
add := func(dir string) {
|
|
if dir == "" {
|
|
return
|
|
}
|
|
path := filepath.Clean(filepath.Join(dir, suffix))
|
|
if !seen[path] {
|
|
seen[path] = true
|
|
candidates = append(candidates, path)
|
|
}
|
|
}
|
|
|
|
add(search.libOllamaPath)
|
|
|
|
addPackagedLayoutDirs := func(base string) {
|
|
if base == "" {
|
|
return
|
|
}
|
|
switch goos {
|
|
case "darwin":
|
|
// macOS tarballs and apps colocate llama.cpp helpers with ollama.
|
|
add(base)
|
|
// Per-architecture local dist output keeps helpers under lib/ollama.
|
|
add(filepath.Join(base, "lib", "ollama"))
|
|
// Standard CMake installs put ollama in bin/ and helpers in ../lib/ollama/.
|
|
add(filepath.Join(base, "..", "lib", "ollama"))
|
|
case "linux":
|
|
// Linux packages install ollama in bin/ and helpers in ../lib/ollama/.
|
|
add(filepath.Join(base, "..", "lib", "ollama"))
|
|
case "windows":
|
|
// Windows packages keep ollama.exe at top level with lib/ as a peer.
|
|
add(filepath.Join(base, "lib", "ollama"))
|
|
// Standard CMake installs put ollama.exe in bin/ and helpers in ../lib/ollama/.
|
|
add(filepath.Join(base, "..", "lib", "ollama"))
|
|
default:
|
|
add(filepath.Join(base, "lib", "ollama"))
|
|
add(filepath.Join(base, "..", "lib", "ollama"))
|
|
}
|
|
}
|
|
|
|
addLocalLayoutDirs := func(base string) {
|
|
if base == "" {
|
|
return
|
|
}
|
|
add(filepath.Join(base, "build", "lib", "ollama"))
|
|
add(filepath.Join(base, "dist", goos+"-"+goarch, "lib", "ollama"))
|
|
if goos+"_"+goarch != goos+"-"+goarch {
|
|
add(filepath.Join(base, "dist", goos+"_"+goarch, "lib", "ollama"))
|
|
}
|
|
if goos == "darwin" {
|
|
add(filepath.Join(base, "dist", "darwin"))
|
|
}
|
|
}
|
|
|
|
if search.executable != "" {
|
|
exeDir := filepath.Dir(search.executable)
|
|
addPackagedLayoutDirs(exeDir)
|
|
addLocalLayoutDirs(exeDir)
|
|
}
|
|
if search.workingDir != "" {
|
|
addLocalLayoutDirs(search.workingDir)
|
|
}
|
|
|
|
addBuildOutputDirs := func(base string) {
|
|
if base == "" {
|
|
return
|
|
}
|
|
matches, _ := filepath.Glob(filepath.Join(base, "build", "llama-server-*", "bin"))
|
|
slices.SortFunc(matches, func(a, b string) int {
|
|
if rank := llamaCppBuildOutputRank(a) - llamaCppBuildOutputRank(b); rank != 0 {
|
|
return rank
|
|
}
|
|
return strings.Compare(a, b)
|
|
})
|
|
for _, m := range matches {
|
|
add(m)
|
|
}
|
|
}
|
|
if search.executable != "" {
|
|
addBuildOutputDirs(filepath.Dir(search.executable))
|
|
}
|
|
addBuildOutputDirs(search.workingDir)
|
|
|
|
return candidates
|
|
}
|
|
|
|
func llamaCppBinaryName(name, goos string) string {
|
|
if goos == "windows" && filepath.Ext(name) == "" {
|
|
return name + ".exe"
|
|
}
|
|
return name
|
|
}
|
|
|
|
func llamaCppBuildOutputRank(path string) int {
|
|
if strings.Contains(path, "llama-server-darwin") ||
|
|
strings.Contains(path, "llama-server-cuda") ||
|
|
strings.Contains(path, "llama-server-rocm") {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|