client.go 2.32 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
3
package api

import (
Jeffrey Morgan's avatar
Jeffrey Morgan committed
4
	"bufio"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
5
6
7
8
9
	"bytes"
	"context"
	"encoding/json"
	"io"
	"net/http"
Michael Yang's avatar
Michael Yang committed
10
	"net/url"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
11
12
13
)

type Client struct {
Michael Yang's avatar
Michael Yang committed
14
15
16
17
18
19
20
21
22
23
24
25
	base url.URL
}

func NewClient(hosts ...string) *Client {
	host := "127.0.0.1:11434"
	if len(hosts) > 0 {
		host = hosts[0]
	}

	return &Client{
		base: url.URL{Scheme: "http", Host: host},
	}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
26
27
}

Michael Yang's avatar
Michael Yang committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
type options struct {
	requestBody  io.Reader
	responseFunc func(bts []byte) error
}

func OptionRequestBody(data any) func(*options) {
	bts, err := json.Marshal(data)
	if err != nil {
		panic(err)
	}

	return func(opts *options) {
		opts.requestBody = bytes.NewReader(bts)
	}
}

func OptionResponseFunc(fn func([]byte) error) func(*options) {
	return func(opts *options) {
		opts.responseFunc = fn
	}
}

func (c *Client) stream(ctx context.Context, method, path string, fns ...func(*options)) error {
	var opts options
	for _, fn := range fns {
		fn(&opts)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
54
55
	}

Michael Yang's avatar
Michael Yang committed
56
	request, err := http.NewRequestWithContext(ctx, method, c.base.JoinPath(path).String(), opts.requestBody)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
57
58
59
60
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
61
62
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Accept", "application/json")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
63

Michael Yang's avatar
Michael Yang committed
64
	response, err := http.DefaultClient.Do(request)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
65
66
67
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
68
	defer response.Body.Close()
Jeffrey Morgan's avatar
Jeffrey Morgan committed
69

Michael Yang's avatar
Michael Yang committed
70
71
72
73
74
75
	if opts.responseFunc != nil {
		scanner := bufio.NewScanner(response.Body)
		for scanner.Scan() {
			if err := opts.responseFunc(scanner.Bytes()); err != nil {
				return err
			}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
76
77
78
		}
	}

Michael Yang's avatar
Michael Yang committed
79
80
	return nil
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
81

Michael Yang's avatar
Michael Yang committed
82
type GenerateResponseFunc func(GenerateResponse) error
Jeffrey Morgan's avatar
Jeffrey Morgan committed
83

Michael Yang's avatar
Michael Yang committed
84
func (c *Client) Generate(ctx context.Context, req *GenerateRequest, fn GenerateResponseFunc) error {
Michael Yang's avatar
Michael Yang committed
85
86
87
88
89
90
91
92
93
94
95
	return c.stream(ctx, http.MethodPost, "/api/generate",
		OptionRequestBody(req),
		OptionResponseFunc(func(bts []byte) error {
			var resp GenerateResponse
			if err := json.Unmarshal(bts, &resp); err != nil {
				return err
			}

			return fn(resp)
		}),
	)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
96
}
Bruce MacDonald's avatar
Bruce MacDonald committed
97

Michael Yang's avatar
Michael Yang committed
98
99
100
type PullProgressFunc func(PullProgress) error

func (c *Client) Pull(ctx context.Context, req *PullRequest, fn PullProgressFunc) error {
Michael Yang's avatar
Michael Yang committed
101
102
103
104
105
106
107
108
	return c.stream(ctx, http.MethodPost, "/api/pull",
		OptionRequestBody(req),
		OptionResponseFunc(func(bts []byte) error {
			var resp PullProgress
			if err := json.Unmarshal(bts, &resp); err != nil {
				return err
			}

Bruce MacDonald's avatar
Bruce MacDonald committed
109
110
111
112
113
			if resp.Error.Message != "" {
				// couldn't pull the model from the directory, proceed anyway
				return nil
			}

Michael Yang's avatar
Michael Yang committed
114
115
116
			return fn(resp)
		}),
	)
Bruce MacDonald's avatar
Bruce MacDonald committed
117
}