forked from ollama/ollama
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This focuses on Windows first, but coudl be used for Mac and possibly linux in the future.
- Loading branch information
Showing
49 changed files
with
2,621 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ ggml-metal.metal | |
.cache | ||
*.exe | ||
.idea | ||
test_data | ||
test_data | ||
*.crt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Ollama App | ||
|
||
## Linux | ||
|
||
TODO | ||
|
||
## MacOS | ||
|
||
TODO | ||
|
||
## Windows | ||
|
||
If you want to build the installer, youll need to install | ||
- https://jrsoftware.org/isinfo.php | ||
|
||
|
||
In the top directory of this repo, run the following powershell script | ||
to build the ollama CLI, ollama app, and ollama installer. | ||
|
||
``` | ||
powershell -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1 | ||
``` |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package assets | ||
|
||
import ( | ||
"embed" | ||
"io/fs" | ||
) | ||
|
||
//go:embed *.ico | ||
var icons embed.FS | ||
|
||
func ListIcons() ([]string, error) { | ||
return fs.Glob(icons, "*") | ||
} | ||
|
||
func GetIcon(filename string) ([]byte, error) { | ||
return icons.ReadFile(filename) | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//go:build !windows | ||
|
||
package lifecycle | ||
|
||
import "fmt" | ||
|
||
func GetStarted() error { | ||
return fmt.Errorf("GetStarted not implemented") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package lifecycle | ||
|
||
import ( | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"syscall" | ||
) | ||
|
||
func GetStarted() error { | ||
const CREATE_NEW_CONSOLE = 0x00000010 | ||
var err error | ||
bannerScript := filepath.Join(AppDir, "ollama_welcome.ps1") | ||
args := []string{ | ||
// TODO once we're signed, the execution policy bypass should be removed | ||
"powershell", "-noexit", "-ExecutionPolicy", "Bypass", "-nologo", "-file", bannerScript, | ||
} | ||
args[0], err = exec.LookPath(args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Make sure the script actually exists | ||
_, err = os.Stat(bannerScript) | ||
if err != nil { | ||
return fmt.Errorf("getting started banner script error %s", err) | ||
} | ||
|
||
slog.Info(fmt.Sprintf("opening getting started terminal with %v", args)) | ||
attrs := &os.ProcAttr{ | ||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, | ||
Sys: &syscall.SysProcAttr{CreationFlags: CREATE_NEW_CONSOLE, HideWindow: false}, | ||
} | ||
proc, err := os.StartProcess(args[0], args, attrs) | ||
|
||
if err != nil { | ||
return fmt.Errorf("unable to start getting started shell %w", err) | ||
} | ||
|
||
slog.Debug(fmt.Sprintf("getting started terminal PID: %d", proc.Pid)) | ||
return proc.Release() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package lifecycle | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"log/slog" | ||
|
||
"github.com/jmorganca/ollama/app/store" | ||
"github.com/jmorganca/ollama/app/tray" | ||
) | ||
|
||
func Run() { | ||
InitLogging() | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
var done chan int | ||
|
||
t, err := tray.NewTray() | ||
if err != nil { | ||
log.Fatalf("Failed to start: %s", err) | ||
} | ||
callbacks := t.GetCallbacks() | ||
|
||
go func() { | ||
slog.Debug("starting callback loop") | ||
for { | ||
select { | ||
case <-callbacks.Quit: | ||
slog.Debug("QUIT called") | ||
t.Quit() | ||
case <-callbacks.Update: | ||
err := DoUpgrade(cancel, done) | ||
if err != nil { | ||
slog.Warn(fmt.Sprintf("upgrade attempt failed: %s", err)) | ||
} | ||
case <-callbacks.ShowLogs: | ||
ShowLogs() | ||
case <-callbacks.DoFirstUse: | ||
err := GetStarted() | ||
if err != nil { | ||
slog.Warn(fmt.Sprintf("Failed to launch getting started shell: %s", err)) | ||
} | ||
} | ||
} | ||
}() | ||
|
||
// Are we first use? | ||
if !store.GetFirstTimeRun() { | ||
slog.Debug("First time run") | ||
err = t.DisplayFirstUseNotification() | ||
if err != nil { | ||
slog.Debug(fmt.Sprintf("XXX failed to display first use notification %v", err)) | ||
} | ||
store.SetFirstTimeRun(true) | ||
} else { | ||
slog.Debug("Not first time, skipping first run notification") | ||
} | ||
|
||
if IsServerRunning(ctx) { | ||
slog.Debug("XXX detected server already running") | ||
// TODO - should we fail fast, try to kill it, or just ignore? | ||
} else { | ||
done, err = SpawnServer(ctx, CLIName) | ||
if err != nil { | ||
// TODO - should we retry in a backoff loop? | ||
// TODO - should we pop up a warning and maybe add a menu item to view application logs? | ||
slog.Error(fmt.Sprintf("Failed to spawn ollama server %s", err)) | ||
done = make(chan int, 1) | ||
done <- 1 | ||
} | ||
} | ||
|
||
StartBackgroundUpdaterChecker(ctx, t.UpdateAvailable) | ||
|
||
t.Run() | ||
cancel() | ||
slog.Info("Waiting for ollama server to shutdown...") | ||
if done != nil { | ||
<-done | ||
} | ||
slog.Info("Ollama app exiting") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package lifecycle | ||
|
||
import ( | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
func InitLogging() { | ||
level := slog.LevelInfo | ||
|
||
if debug := os.Getenv("OLLAMA_DEBUG"); debug != "" { | ||
level = slog.LevelDebug | ||
} | ||
|
||
var logFile *os.File | ||
var err error | ||
// Detect if we're a GUI app on windows, and if not, send logs to console | ||
if os.Stderr.Fd() != 0 { | ||
// Console app detected | ||
logFile = os.Stderr | ||
// TODO - write one-line to the app.log file saying we're running in console mode to help avoid confusion | ||
} else { | ||
logFile, err = os.OpenFile(AppLogFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755) | ||
if err != nil { | ||
slog.Error(fmt.Sprintf("failed to create server log %v", err)) | ||
return | ||
} | ||
} | ||
handler := slog.NewTextHandler(logFile, &slog.HandlerOptions{ | ||
Level: level, | ||
AddSource: true, | ||
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr { | ||
if attr.Key == slog.SourceKey { | ||
source := attr.Value.Any().(*slog.Source) | ||
source.File = filepath.Base(source.File) | ||
} | ||
return attr | ||
}, | ||
}) | ||
|
||
slog.SetDefault(slog.New(handler)) | ||
|
||
slog.Info("ollama app started") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//go:build !windows | ||
|
||
package lifecycle | ||
|
||
import "log/slog" | ||
|
||
func ShowLogs() { | ||
slog.Warn("ShowLogs not yet implemented") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package lifecycle | ||
|
||
import ( | ||
"fmt" | ||
"log/slog" | ||
"os/exec" | ||
"syscall" | ||
) | ||
|
||
func ShowLogs() { | ||
cmd_path := "c:\\Windows\\system32\\cmd.exe" | ||
slog.Debug(fmt.Sprintf("viewing logs with start %s", AppDataDir)) | ||
cmd := exec.Command(cmd_path, "/c", "start", AppDataDir) | ||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true, CreationFlags: 0x08000000} | ||
err := cmd.Start() | ||
if err != nil { | ||
slog.Error(fmt.Sprintf("Failed to open log dir: %s", err)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package lifecycle | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
var ( | ||
AppName = "ollama app" | ||
CLIName = "ollama" | ||
AppDir = "/opt/Ollama" | ||
AppDataDir = "/opt/Ollama" | ||
// TODO - should there be a distinct log dir? | ||
UpdateStageDir = "/tmp" | ||
AppLogFile = "/tmp/ollama_app.log" | ||
ServerLogFile = "/tmp/ollama.log" | ||
UpgradeLogFile = "/tmp/ollama_update.log" | ||
Installer = "OllamaSetup.exe" | ||
) | ||
|
||
func init() { | ||
if runtime.GOOS == "windows" { | ||
AppName += ".exe" | ||
CLIName += ".exe" | ||
// Logs, configs, downloads go to LOCALAPPDATA | ||
localAppData := os.Getenv("LOCALAPPDATA") | ||
AppDataDir = filepath.Join(localAppData, "Ollama") | ||
UpdateStageDir = filepath.Join(AppDataDir, "updates") | ||
AppLogFile = filepath.Join(AppDataDir, "app.log") | ||
ServerLogFile = filepath.Join(AppDataDir, "server.log") | ||
UpgradeLogFile = filepath.Join(AppDataDir, "upgrade.log") | ||
|
||
// Executables are stored in APPDATA | ||
AppDir = filepath.Join(localAppData, "Programs", "Ollama") | ||
|
||
// Make sure we have PATH set correctly for any spawned children | ||
paths := strings.Split(os.Getenv("PATH"), ";") | ||
// Start with whatever we find in the PATH/LD_LIBRARY_PATH | ||
found := false | ||
for _, path := range paths { | ||
d, err := filepath.Abs(path) | ||
if err != nil { | ||
continue | ||
} | ||
if strings.EqualFold(AppDir, d) { | ||
found = true | ||
} | ||
} | ||
if !found { | ||
paths = append(paths, AppDir) | ||
|
||
pathVal := strings.Join(paths, ";") | ||
slog.Debug("setting PATH=" + pathVal) | ||
err := os.Setenv("PATH", pathVal) | ||
if err != nil { | ||
slog.Error(fmt.Sprintf("failed to update PATH: %s", err)) | ||
} | ||
} | ||
|
||
// Make sure our logging dir exists | ||
_, err := os.Stat(AppDataDir) | ||
if errors.Is(err, os.ErrNotExist) { | ||
if err := os.MkdirAll(AppDataDir, 0o755); err != nil { | ||
slog.Error(fmt.Sprintf("create ollama dir %s: %v", AppDataDir, err)) | ||
} | ||
} | ||
|
||
} else if runtime.GOOS == "darwin" { | ||
// TODO | ||
AppName += ".app" | ||
// } else if runtime.GOOS == "linux" { | ||
// TODO | ||
} | ||
} |
Oops, something went wrong.