浏览代码

check server is running before running command

Bruce MacDonald 1 年之前
父节点
当前提交
8f8b6288ac
共有 3 个文件被更改,包括 91 次插入25 次删除
  1. 7 0
      api/client.go
  2. 81 25
      cmd/cmd.go
  3. 3 0
      server/routes.go

+ 7 - 0
api/client.go

@@ -223,3 +223,10 @@ func (c *Client) Delete(ctx context.Context, req *DeleteRequest) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func (c *Client) Heartbeat(ctx context.Context) error {
+	if err := c.do(ctx, http.MethodHead, "/", nil, nil); err != nil {
+		return err
+	}
+	return nil
+}

+ 81 - 25
cmd/cmd.go

@@ -10,7 +10,9 @@ import (
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -425,7 +427,6 @@ func generateInteractive(cmd *cobra.Command, model string) error {
 							usage()
 							usage()
 							continue
 							continue
 						}
 						}
-
 					} else {
 					} else {
 						usage()
 						usage()
 						continue
 						continue
@@ -519,6 +520,54 @@ func RunServer(_ *cobra.Command, _ []string) error {
 	return server.Serve(ln)
 	return server.Serve(ln)
 }
 }
 
 
+func startMacApp(client *api.Client) error {
+	exe, err := os.Executable()
+	if err != nil {
+		return err
+	}
+	link, err := os.Readlink(exe)
+	if err != nil {
+		return err
+	}
+	if !strings.Contains(link, "Ollama.app") {
+		return fmt.Errorf("could not find ollama app")
+	}
+	path := strings.Split(link, "Ollama.app")
+	if err := exec.Command("/usr/bin/open", "-a", path[0]+"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
+			}
+		}
+	}
+}
+
+func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
+	client := api.NewClient()
+	if err := client.Heartbeat(context.Background()); err != nil {
+		if !strings.Contains(err.Error(), "connection refused") {
+			return err
+		}
+		if runtime.GOOS == "darwin" {
+			if err := startMacApp(client); err != nil {
+				return fmt.Errorf("could not connect to ollama app, is it running?")
+			}
+		} else {
+			return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
+		}
+	}
+	return nil
+}
+
 func NewCLI() *cobra.Command {
 func NewCLI() *cobra.Command {
 	log.SetFlags(log.LstdFlags | log.Lshortfile)
 	log.SetFlags(log.LstdFlags | log.Lshortfile)
 
 
@@ -534,19 +583,21 @@ func NewCLI() *cobra.Command {
 	cobra.EnableCommandSorting = false
 	cobra.EnableCommandSorting = false
 
 
 	createCmd := &cobra.Command{
 	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\")")
 	createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
 
 
 	runCmd := &cobra.Command{
 	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")
 	runCmd.Flags().Bool("verbose", false, "Show timings for response")
@@ -559,19 +610,21 @@ func NewCLI() *cobra.Command {
 	}
 	}
 
 
 	pullCmd := &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")
 	pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
 
 
 	pushCmd := &cobra.Command{
 	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")
 	pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
@@ -580,21 +633,24 @@ func NewCLI() *cobra.Command {
 		Use:     "list",
 		Use:     "list",
 		Aliases: []string{"ls"},
 		Aliases: []string{"ls"},
 		Short:   "List models",
 		Short:   "List models",
+		PreRunE: checkServerHeartbeat,
 		RunE:    ListHandler,
 		RunE:    ListHandler,
 	}
 	}
 
 
 	copyCmd := &cobra.Command{
 	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{
 	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(
 	rootCmd.AddCommand(

+ 3 - 0
server/routes.go

@@ -318,6 +318,9 @@ func Serve(ln net.Listener) error {
 	r.GET("/", func(c *gin.Context) {
 	r.GET("/", func(c *gin.Context) {
 		c.String(http.StatusOK, "Ollama is running")
 		c.String(http.StatusOK, "Ollama is running")
 	})
 	})
+	r.HEAD("/", func(c *gin.Context) {
+		c.Status(http.StatusOK)
+	})
 
 
 	r.POST("/api/pull", PullModelHandler)
 	r.POST("/api/pull", PullModelHandler)
 	r.POST("/api/generate", GenerateHandler)
 	r.POST("/api/generate", GenerateHandler)