diff --git a/api/client.go b/api/client.go index 6786fa48..ed1053e8 100644 --- a/api/client.go +++ b/api/client.go @@ -223,3 +223,10 @@ func (c *Client) Delete(ctx context.Context, req *DeleteRequest) error { } return nil } + +func (c *Client) Heartbeat(ctx context.Context) error { + if err := c.do(ctx, http.MethodGet, "/", nil, nil); err != nil { + return err + } + return nil +} diff --git a/cmd/cmd.go b/cmd/cmd.go index 61658f87..fa06e85b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -10,7 +10,9 @@ import ( "net" "net/http" "os" + "os/exec" "path/filepath" + "runtime" "strings" "time" @@ -431,7 +433,6 @@ func generateInteractive(cmd *cobra.Command, model string) error { usage() continue } - } else { usage() continue @@ -525,6 +526,38 @@ func RunServer(_ *cobra.Command, _ []string) error { return server.Serve(ln) } +func checkServerHeartbeat(_ *cobra.Command, _ []string) error { + client := api.NewClient() + if err := client.Heartbeat(context.Background()); err != nil { + if strings.Contains(err.Error(), "connection refused") { + if runtime.GOOS == "darwin" { + // if the mac app is available, start it + if _, err := os.Stat("/Applications/Ollama.app"); err == nil { + if err := exec.Command("/usr/bin/open", "-a", "/Applications/Ollama.app").Run(); err != nil { + return err + } + // wait for the server to start + timeout := time.After(5 * time.Second) + tick := time.Tick(500 * time.Millisecond) + for { + select { + case <-timeout: + return errors.New("timed out waiting for server to start") + case <-tick: + if err := client.Heartbeat(context.Background()); err == nil { + return nil // server has started + } + } + } + } + } + return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it") + } + return err + } + return nil +} + func NewCLI() *cobra.Command { log.SetFlags(log.LstdFlags | log.Lshortfile) @@ -540,19 +573,21 @@ func NewCLI() *cobra.Command { cobra.EnableCommandSorting = false createCmd := &cobra.Command{ - Use: "create MODEL", - Short: "Create a model from a Modelfile", - Args: cobra.MinimumNArgs(1), - RunE: CreateHandler, + Use: "create MODEL", + Short: "Create a model from a Modelfile", + Args: cobra.MinimumNArgs(1), + PreRunE: checkServerHeartbeat, + RunE: CreateHandler, } createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")") runCmd := &cobra.Command{ - Use: "run MODEL [PROMPT]", - Short: "Run a model", - Args: cobra.MinimumNArgs(1), - RunE: RunHandler, + Use: "run MODEL [PROMPT]", + Short: "Run a model", + Args: cobra.MinimumNArgs(1), + PreRunE: checkServerHeartbeat, + RunE: RunHandler, } runCmd.Flags().Bool("verbose", false, "Show timings for response") @@ -565,19 +600,21 @@ func NewCLI() *cobra.Command { } pullCmd := &cobra.Command{ - Use: "pull MODEL", - Short: "Pull a model from a registry", - Args: cobra.MinimumNArgs(1), - RunE: PullHandler, + Use: "pull MODEL", + Short: "Pull a model from a registry", + Args: cobra.MinimumNArgs(1), + PreRunE: checkServerHeartbeat, + RunE: PullHandler, } pullCmd.Flags().Bool("insecure", false, "Use an insecure registry") pushCmd := &cobra.Command{ - Use: "push MODEL", - Short: "Push a model to a registry", - Args: cobra.MinimumNArgs(1), - RunE: PushHandler, + Use: "push MODEL", + Short: "Push a model to a registry", + Args: cobra.MinimumNArgs(1), + PreRunE: checkServerHeartbeat, + RunE: PushHandler, } pushCmd.Flags().Bool("insecure", false, "Use an insecure registry") @@ -586,21 +623,24 @@ func NewCLI() *cobra.Command { Use: "list", Aliases: []string{"ls"}, Short: "List models", + PreRunE: checkServerHeartbeat, RunE: ListHandler, } copyCmd := &cobra.Command{ - Use: "cp", - Short: "Copy a model", - Args: cobra.MinimumNArgs(2), - RunE: CopyHandler, + Use: "cp", + Short: "Copy a model", + Args: cobra.MinimumNArgs(2), + PreRunE: checkServerHeartbeat, + RunE: CopyHandler, } deleteCmd := &cobra.Command{ - Use: "rm", - Short: "Remove a model", - Args: cobra.MinimumNArgs(1), - RunE: DeleteHandler, + Use: "rm", + Short: "Remove a model", + Args: cobra.MinimumNArgs(1), + PreRunE: checkServerHeartbeat, + RunE: DeleteHandler, } rootCmd.AddCommand(