routes.go 3.81 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
3
package server

import (
Michael Yang's avatar
Michael Yang committed
4
	"embed"
Michael Yang's avatar
Michael Yang committed
5
	"encoding/json"
6
	"errors"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
7
8
	"io"
	"log"
Michael Yang's avatar
Michael Yang committed
9
	"math"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
10
11
	"net"
	"net/http"
12
	"os"
Michael Yang's avatar
Michael Yang committed
13
14
15
	"path"
	"strings"
	"text/template"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
16
17

	"github.com/gin-gonic/gin"
Michael Yang's avatar
Michael Yang committed
18
	"github.com/lithammer/fuzzysearch/fuzzy"
Michael Yang's avatar
Michael Yang committed
19
	"golang.org/x/sync/errgroup"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
20

Jeffrey Morgan's avatar
Jeffrey Morgan committed
21
	"github.com/jmorganca/ollama/api"
Michael Yang's avatar
Michael Yang committed
22
	"github.com/jmorganca/ollama/llama"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
23
24
)

Michael Yang's avatar
Michael Yang committed
25
26
27
//go:embed templates/*
var templatesFS embed.FS
var templates = template.Must(template.ParseFS(templatesFS, "templates/*.prompt"))
Michael Yang's avatar
Michael Yang committed
28

29
30
31
32
33
34
35
36
37
func cacheDir() string {
	home, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	return path.Join(home, ".ollama")
}

Bruce MacDonald's avatar
Bruce MacDonald committed
38
func generate(c *gin.Context) {
Michael Yang's avatar
Michael Yang committed
39
40
	req := api.GenerateRequest{
		Options: api.DefaultOptions(),
41
42
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
43
	if err := c.ShouldBindJSON(&req); err != nil {
44
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
Bruce MacDonald's avatar
Bruce MacDonald committed
45
46
		return
	}
47

Bruce MacDonald's avatar
Bruce MacDonald committed
48
	if remoteModel, _ := getRemote(req.Model); remoteModel != nil {
Michael Yang's avatar
Michael Yang committed
49
50
		req.Model = remoteModel.FullName()
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
51
52
	if _, err := os.Stat(req.Model); err != nil {
		if !errors.Is(err, os.ErrNotExist) {
53
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
Bruce MacDonald's avatar
Bruce MacDonald committed
54
55
56
57
			return
		}
		req.Model = path.Join(cacheDir(), "models", req.Model+".bin")
	}
Michael Yang's avatar
Michael Yang committed
58

Michael Yang's avatar
Michael Yang committed
59
	llm, err := llama.New(req.Model, req.Options)
Bruce MacDonald's avatar
Bruce MacDonald committed
60
	if err != nil {
Michael Yang's avatar
Michael Yang committed
61
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
Bruce MacDonald's avatar
Bruce MacDonald committed
62
63
		return
	}
Michael Yang's avatar
Michael Yang committed
64
	defer llm.Close()
Jeffrey Morgan's avatar
Jeffrey Morgan committed
65

Michael Yang's avatar
Michael Yang committed
66
67
68
69
70
	templateNames := make([]string, 0, len(templates.Templates()))
	for _, template := range templates.Templates() {
		templateNames = append(templateNames, template.Name())
	}

Michael Yang's avatar
Michael Yang committed
71
	match, _ := matchRankOne(path.Base(req.Model), templateNames)
Michael Yang's avatar
Michael Yang committed
72
73
74
	if template := templates.Lookup(match); template != nil {
		var sb strings.Builder
		if err := template.Execute(&sb, req); err != nil {
75
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
Michael Yang's avatar
Michael Yang committed
76
77
78
79
80
81
			return
		}

		req.Prompt = sb.String()
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
82
	ch := make(chan string)
Michael Yang's avatar
Michael Yang committed
83
84
	g, _ := errgroup.WithContext(c.Request.Context())
	g.Go(func() error {
Bruce MacDonald's avatar
Bruce MacDonald committed
85
		defer close(ch)
Michael Yang's avatar
Michael Yang committed
86
87
88
89
		return llm.Predict(req.Prompt, func(s string) {
			ch <- s
		})
	})
Jeffrey Morgan's avatar
Jeffrey Morgan committed
90

Michael Yang's avatar
Michael Yang committed
91
92
93
94
95
96
	g.Go(func() error {
		c.Stream(func(w io.Writer) bool {
			s, ok := <-ch
			if !ok {
				return false
			}
Michael Yang's avatar
Michael Yang committed
97

Michael Yang's avatar
Michael Yang committed
98
99
100
101
			bts, err := json.Marshal(api.GenerateResponse{Response: s})
			if err != nil {
				return false
			}
Michael Yang's avatar
Michael Yang committed
102

Michael Yang's avatar
Michael Yang committed
103
104
105
106
			bts = append(bts, '\n')
			if _, err := w.Write(bts); err != nil {
				return false
			}
Michael Yang's avatar
Michael Yang committed
107

Michael Yang's avatar
Michael Yang committed
108
109
			return true
		})
Michael Yang's avatar
Michael Yang committed
110

Michael Yang's avatar
Michael Yang committed
111
		return nil
Jeffrey Morgan's avatar
Jeffrey Morgan committed
112
	})
Michael Yang's avatar
Michael Yang committed
113
114
115
116
117

	if err := g.Wait(); err != nil && !errors.Is(err, io.EOF) {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
118
119
120
121
122
}

func Serve(ln net.Listener) error {
	r := gin.Default()

123
124
125
126
	r.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Ollama is running")
	})

Bruce MacDonald's avatar
Bruce MacDonald committed
127
128
129
	r.POST("api/pull", func(c *gin.Context) {
		var req api.PullRequest
		if err := c.ShouldBindJSON(&req); err != nil {
130
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
Bruce MacDonald's avatar
Bruce MacDonald committed
131
132
133
			return
		}

Bruce MacDonald's avatar
Bruce MacDonald committed
134
		progressCh := make(chan api.PullProgress)
Bruce MacDonald's avatar
Bruce MacDonald committed
135
136
137
		go func() {
			defer close(progressCh)
			if err := pull(req.Model, progressCh); err != nil {
138
139
				var opError *net.OpError
				if errors.As(err, &opError) {
140
					c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
141
142
					return
				}
143
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
Bruce MacDonald's avatar
Bruce MacDonald committed
144
145
146
147
148
149
150
151
152
				return
			}
		}()

		c.Stream(func(w io.Writer) bool {
			progress, ok := <-progressCh
			if !ok {
				return false
			}
Michael Yang's avatar
Michael Yang committed
153
154
155
156
157
158
159
160
161
162
163

			bts, err := json.Marshal(progress)
			if err != nil {
				return false
			}

			bts = append(bts, '\n')
			if _, err := w.Write(bts); err != nil {
				return false
			}

Bruce MacDonald's avatar
Bruce MacDonald committed
164
165
166
			return true
		})
	})
Bruce MacDonald's avatar
Bruce MacDonald committed
167
168

	r.POST("/api/generate", generate)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
169
170
171
172
173
174
175
176

	log.Printf("Listening on %s", ln.Addr())
	s := &http.Server{
		Handler: r,
	}

	return s.Serve(ln)
}
Michael Yang's avatar
Michael Yang committed
177
178

func matchRankOne(source string, targets []string) (bestMatch string, bestRank int) {
Michael Yang's avatar
Michael Yang committed
179
	bestRank = math.MaxInt
Michael Yang's avatar
Michael Yang committed
180
	for _, target := range targets {
Michael Yang's avatar
Michael Yang committed
181
		if rank := fuzzy.LevenshteinDistance(source, target); bestRank > rank {
Michael Yang's avatar
Michael Yang committed
182
183
184
185
186
187
188
			bestRank = rank
			bestMatch = target
		}
	}

	return
}