client.go 2.02 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
	"bytes"
	"context"
	"encoding/json"
8
	"fmt"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
9
	"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
}

28
29
30
31
32
33
34
func (c *Client) stream(ctx context.Context, method, path string, data any, callback func([]byte) error) error {
	var buf *bytes.Buffer
	if data != nil {
		bts, err := json.Marshal(data)
		if err != nil {
			return err
		}
Michael Yang's avatar
Michael Yang committed
35

36
		buf = bytes.NewBuffer(bts)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
37
38
	}

39
	request, err := http.NewRequestWithContext(ctx, method, c.base.JoinPath(path).String(), buf)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
40
41
42
43
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
44
45
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Accept", "application/json")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
46

Michael Yang's avatar
Michael Yang committed
47
	response, err := http.DefaultClient.Do(request)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
48
49
50
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
51
	defer response.Body.Close()
Jeffrey Morgan's avatar
Jeffrey Morgan committed
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	scanner := bufio.NewScanner(response.Body)
	for scanner.Scan() {
		var errorResponse struct {
			Error string `json:"error"`
		}

		bts := scanner.Bytes()
		if err := json.Unmarshal(bts, &errorResponse); err != nil {
			return fmt.Errorf("unmarshal: %w", err)
		}

		if len(errorResponse.Error) > 0 {
			return fmt.Errorf("stream: %s", errorResponse.Error)
		}

		if err := callback(bts); err != nil {
			return err
Jeffrey Morgan's avatar
Jeffrey Morgan committed
70
71
72
		}
	}

Michael Yang's avatar
Michael Yang committed
73
74
	return nil
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
75

Michael Yang's avatar
Michael Yang committed
76
type GenerateResponseFunc func(GenerateResponse) error
Jeffrey Morgan's avatar
Jeffrey Morgan committed
77

Michael Yang's avatar
Michael Yang committed
78
func (c *Client) Generate(ctx context.Context, req *GenerateRequest, fn GenerateResponseFunc) error {
79
80
81
82
83
84
85
86
	return c.stream(ctx, http.MethodPost, "/api/generate", req, 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
87
}
Bruce MacDonald's avatar
Bruce MacDonald committed
88

Michael Yang's avatar
Michael Yang committed
89
90
91
type PullProgressFunc func(PullProgress) error

func (c *Client) Pull(ctx context.Context, req *PullRequest, fn PullProgressFunc) error {
92
93
94
95
96
97
98
99
	return c.stream(ctx, http.MethodPost, "/api/pull", req, func(bts []byte) error {
		var resp PullProgress
		if err := json.Unmarshal(bts, &resp); err != nil {
			return err
		}

		return fn(resp)
	})
Bruce MacDonald's avatar
Bruce MacDonald committed
100
}