"git@developer.sourcefind.cn:zhaoyu6/sglang.git" did not exist on "c411f32e1c9b551011a52566b5afae1320a99fde"
routes.go 7.71 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
3
package server

import (
Michael Yang's avatar
Michael Yang committed
4
	"encoding/json"
5
	"errors"
6
	"fmt"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
7
8
9
10
	"io"
	"log"
	"net"
	"net/http"
11
	"os"
Michael Yang's avatar
Michael Yang committed
12
	"path/filepath"
Michael Yang's avatar
Michael Yang committed
13
	"strings"
Michael Yang's avatar
Michael Yang committed
14
	"sync"
15
	"time"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
16

Michael Yang's avatar
Michael Yang committed
17
	"dario.cat/mergo"
Michael Yang's avatar
Michael Yang committed
18
	"github.com/gin-contrib/cors"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
19
20
	"github.com/gin-gonic/gin"

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
var activeSession struct {
Michael Yang's avatar
Michael Yang committed
26
27
28
29
30
31
32
	mu sync.Mutex

	id  int64
	llm *llama.LLM

	expireAt    time.Time
	expireTimer *time.Timer
Michael Yang's avatar
Michael Yang committed
33
34
}

35
func GenerateHandler(c *gin.Context) {
Michael Yang's avatar
Michael Yang committed
36
37
	activeSession.mu.Lock()
	defer activeSession.mu.Unlock()
Michael Yang's avatar
Michael Yang committed
38

Michael Yang's avatar
Michael Yang committed
39
	checkpointStart := time.Now()
40

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

47
48
49
50
	model, err := GetModel(req.Model)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
Bruce MacDonald's avatar
Bruce MacDonald committed
51
	}
Michael Yang's avatar
Michael Yang committed
52

Michael Yang's avatar
Michael Yang committed
53
54
55
56
	if req.SessionID == 0 || req.SessionID != activeSession.id {
		if activeSession.llm != nil {
			activeSession.llm.Close()
			activeSession.llm = nil
Michael Yang's avatar
Michael Yang committed
57
		}
Michael Yang's avatar
Michael Yang committed
58

Michael Yang's avatar
Michael Yang committed
59
60
61
62
63
		opts := api.DefaultOptions()
		if err := mergo.Merge(&opts, model.Options, mergo.WithOverride); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
Michael Yang's avatar
Michael Yang committed
64

Michael Yang's avatar
Michael Yang committed
65
66
67
68
69
70
71
72
73
74
75
		if err := mergo.Merge(&opts, req.Options, mergo.WithOverride); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		llm, err := llama.New(model.ModelPath, opts)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

Michael Yang's avatar
Michael Yang committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
		activeSession.id = time.Now().UnixNano()
		activeSession.llm = llm
	}

	sessionDuration := req.SessionDuration
	sessionID := activeSession.id

	activeSession.expireAt = time.Now().Add(sessionDuration.Duration)
	if activeSession.expireTimer == nil {
		activeSession.expireTimer = time.AfterFunc(sessionDuration.Duration, func() {
			activeSession.mu.Lock()
			defer activeSession.mu.Unlock()

			if sessionID != activeSession.id {
				return
			}

			if time.Now().Before(activeSession.expireAt) {
				return
			}

			activeSession.llm.Close()
			activeSession.llm = nil
			activeSession.id = 0
		})
Michael Yang's avatar
Michael Yang committed
101
	}
Michael Yang's avatar
Michael Yang committed
102
	activeSession.expireTimer.Reset(sessionDuration.Duration)
Michael Yang's avatar
Michael Yang committed
103

Michael Yang's avatar
Michael Yang committed
104
105
	checkpointLoaded := time.Now()

Michael Yang's avatar
Michael Yang committed
106
	prompt, err := model.Prompt(req)
Michael Yang's avatar
Michael Yang committed
107
108
109
110
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
111

Michael Yang's avatar
Michael Yang committed
112
113
114
	ch := make(chan any)
	go func() {
		defer close(ch)
Michael Yang's avatar
Michael Yang committed
115
		fn := func(r api.GenerateResponse) {
Michael Yang's avatar
Michael Yang committed
116
117
118
			activeSession.expireAt = time.Now().Add(sessionDuration.Duration)
			activeSession.expireTimer.Reset(sessionDuration.Duration)

Michael Yang's avatar
Michael Yang committed
119
120
			r.Model = req.Model
			r.CreatedAt = time.Now().UTC()
Michael Yang's avatar
Michael Yang committed
121
122
			r.SessionID = activeSession.id
			r.SessionExpiresAt = activeSession.expireAt.UTC()
Michael Yang's avatar
Michael Yang committed
123
			if r.Done {
Michael Yang's avatar
Michael Yang committed
124
125
				r.TotalDuration = time.Since(checkpointStart)
				r.LoadDuration = checkpointLoaded.Sub(checkpointStart)
Michael Yang's avatar
Michael Yang committed
126
127
128
			}

			ch <- r
Michael Yang's avatar
Michael Yang committed
129
130
		}

Michael Yang's avatar
Michael Yang committed
131
		if err := activeSession.llm.Predict(req.Context, prompt, fn); err != nil {
Michael Yang's avatar
Michael Yang committed
132
133
			ch <- gin.H{"error": err.Error()}
		}
Michael Yang's avatar
Michael Yang committed
134
	}()
Michael Yang's avatar
Michael Yang committed
135

Michael Yang's avatar
Michael Yang committed
136
	streamResponse(c, ch)
Michael Yang's avatar
Michael Yang committed
137
}
Michael Yang's avatar
Michael Yang committed
138

139
func PullModelHandler(c *gin.Context) {
Michael Yang's avatar
Michael Yang committed
140
141
142
143
144
145
	var req api.PullRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

146
147
148
	ch := make(chan any)
	go func() {
		defer close(ch)
149
150
		fn := func(r api.ProgressResponse) {
			ch <- r
151
		}
152

153
154
155
156
157
158
159
		regOpts := &RegistryOptions{
			Insecure: req.Insecure,
			Username: req.Username,
			Password: req.Password,
		}

		if err := PullModel(req.Name, regOpts, fn); err != nil {
Michael Yang's avatar
Michael Yang committed
160
			ch <- gin.H{"error": err.Error()}
161
162
163
164
165
166
		}
	}()

	streamResponse(c, ch)
}

167
func PushModelHandler(c *gin.Context) {
168
169
170
	var req api.PushRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
Michael Yang's avatar
Michael Yang committed
171
172
		return
	}
Michael Yang's avatar
Michael Yang committed
173

174
175
176
	ch := make(chan any)
	go func() {
		defer close(ch)
177
178
		fn := func(r api.ProgressResponse) {
			ch <- r
179
		}
180

181
182
183
184
185
186
187
		regOpts := &RegistryOptions{
			Insecure: req.Insecure,
			Username: req.Username,
			Password: req.Password,
		}

		if err := PushModel(req.Name, regOpts, fn); err != nil {
Michael Yang's avatar
Michael Yang committed
188
			ch <- gin.H{"error": err.Error()}
189
190
191
192
193
194
		}
	}()

	streamResponse(c, ch)
}

195
func CreateModelHandler(c *gin.Context) {
196
197
198
	var req api.CreateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
Michael Yang's avatar
Michael Yang committed
199
		return
200
201
	}

Michael Yang's avatar
Michael Yang committed
202
	ch := make(chan any)
Michael Yang's avatar
Michael Yang committed
203
204
	go func() {
		defer close(ch)
205
206
		fn := func(resp api.ProgressResponse) {
			ch <- resp
207
208
		}

209
		if err := CreateModel(req.Name, req.Path, fn); err != nil {
Michael Yang's avatar
Michael Yang committed
210
			ch <- gin.H{"error": err.Error()}
211
		}
Michael Yang's avatar
Michael Yang committed
212
	}()
Michael Yang's avatar
Michael Yang committed
213

Michael Yang's avatar
Michael Yang committed
214
	streamResponse(c, ch)
Bruce MacDonald's avatar
Bruce MacDonald committed
215
216
}

217
218
219
220
221
222
223
func DeleteModelHandler(c *gin.Context) {
	var req api.DeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

224
225
226
227
	if err := DeleteModel(req.Name); err != nil {
		if os.IsNotExist(err) {
			c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("model '%s' not found", req.Name)})
		} else {
228
229
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
230
231
		return
	}
232
233
234
}

func ListModelsHandler(c *gin.Context) {
Patrick Devine's avatar
Patrick Devine committed
235
236
237
238
239
240
241
242
	var models []api.ListResponseModel
	fp, err := GetManifestPath()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	err = filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
		if err != nil {
243
244
245
246
			if errors.Is(err, os.ErrNotExist) {
				log.Printf("manifest file does not exist: %s", fp)
				return nil
			}
Patrick Devine's avatar
Patrick Devine committed
247
248
249
250
251
			return err
		}
		if !info.IsDir() {
			fi, err := os.Stat(path)
			if err != nil {
252
253
				log.Printf("skipping file: %s", fp)
				return nil
Patrick Devine's avatar
Patrick Devine committed
254
255
256
257
258
259
260
261
262
263
			}
			path := path[len(fp)+1:]
			slashIndex := strings.LastIndex(path, "/")
			if slashIndex == -1 {
				return nil
			}
			tag := path[:slashIndex] + ":" + path[slashIndex+1:]
			mp := ParseModelPath(tag)
			manifest, err := GetManifest(mp)
			if err != nil {
264
265
				log.Printf("skipping file: %s", fp)
				return nil
Patrick Devine's avatar
Patrick Devine committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
			}
			model := api.ListResponseModel{
				Name:       mp.GetShortTagname(),
				Size:       manifest.GetTotalSize(),
				ModifiedAt: fi.ModTime(),
			}
			models = append(models, model)
		}
		return nil
	})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

Michael Yang's avatar
Michael Yang committed
281
	c.JSON(http.StatusOK, api.ListResponse{Models: models})
Patrick Devine's avatar
Patrick Devine committed
282
283
}

Patrick Devine's avatar
Patrick Devine committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
func CopyModelHandler(c *gin.Context) {
	var req api.CopyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if err := CopyModel(req.Source, req.Destination); err != nil {
		if os.IsNotExist(err) {
			c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("model '%s' not found", req.Source)})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}
}

Bruce MacDonald's avatar
Bruce MacDonald committed
301
func Serve(ln net.Listener) error {
Michael Yang's avatar
Michael Yang committed
302
303
304
305
306
307
308
309
310
311
312
313
314
315
	config := cors.DefaultConfig()
	config.AllowWildcard = true
	// only allow http/https from localhost
	config.AllowOrigins = []string{
		"http://localhost",
		"http://localhost:*",
		"https://localhost",
		"https://localhost:*",
		"http://127.0.0.1",
		"http://127.0.0.1:*",
		"https://127.0.0.1",
		"https://127.0.0.1:*",
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
316
	r := gin.Default()
Michael Yang's avatar
Michael Yang committed
317
	r.Use(cors.New(config))
Bruce MacDonald's avatar
Bruce MacDonald committed
318

319
320
321
322
	r.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Ollama is running")
	})

323
324
325
326
	r.POST("/api/pull", PullModelHandler)
	r.POST("/api/generate", GenerateHandler)
	r.POST("/api/create", CreateModelHandler)
	r.POST("/api/push", PushModelHandler)
Patrick Devine's avatar
Patrick Devine committed
327
	r.POST("/api/copy", CopyModelHandler)
328
329
	r.GET("/api/tags", ListModelsHandler)
	r.DELETE("/api/delete", DeleteModelHandler)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
330
331
332
333
334
335
336
337

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

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

Michael Yang's avatar
Michael Yang committed
339
func streamResponse(c *gin.Context, ch chan any) {
Michael Yang's avatar
Michael Yang committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
	c.Stream(func(w io.Writer) bool {
		val, ok := <-ch
		if !ok {
			return false
		}

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

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

		return true
	})
}