Skip to content

Commit c7c8a8e

Browse files
committed
Added image method
1 parent e1e4617 commit c7c8a8e

19 files changed

+1157
-23
lines changed

cmd/cli/flags.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ import (
55
"os"
66
"time"
77

8+
// Packages
89
"github.com/djthorpe/go-errors"
10+
"github.com/mutablelogic/go-client/pkg/writer"
911
)
1012

1113
///////////////////////////////////////////////////////////////////////////////
1214
// TYPES
1315

1416
type Flags struct {
1517
*flag.FlagSet
18+
19+
writer *writer.Writer
1620
}
1721

1822
type FlagsRegister func(*Flags)
@@ -36,6 +40,13 @@ func NewFlags(name string, args []string, register ...FlagsRegister) (*Flags, er
3640
return nil, err
3741
}
3842

43+
// Create a writer
44+
if w, err := writer.New(os.Stdout); err != nil {
45+
return nil, err
46+
} else {
47+
flags.writer = w
48+
}
49+
3950
// Return success
4051
return flags, nil
4152
}
@@ -58,3 +69,7 @@ func (flags *Flags) GetString(key string) (string, error) {
5869
return os.ExpandEnv(flag.Value.String()), nil
5970
}
6071
}
72+
73+
func (flags *Flags) Write(v writer.TableWriter) error {
74+
return flags.writer.Write(v)
75+
}

cmd/cli/ipify.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"fmt"
54

65
// Packages
76
"github.com/mutablelogic/go-client/pkg/client"
@@ -29,11 +28,10 @@ func IpifyRegister(cmd []Client, opts []client.ClientOpt, flags *Flags) ([]Clien
2928

3029
func IpifyGet(ipify *ipify.Client, flags *Flags) CommandFn {
3130
return func() error {
32-
if addr, err := ipify.Get(); err != nil {
31+
addr, err := ipify.Get()
32+
if err != nil {
3333
return err
34-
} else {
35-
fmt.Printf("IP Address: %s\n", addr)
3634
}
37-
return nil
35+
return flags.Write(addr)
3836
}
3937
}

pkg/client/client.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,41 @@ func (client *Client) Do(in Payload, out any, opts ...RequestOpt) error {
156156
}
157157

158158
// If client token is set, then add to request
159-
if client.token.Scheme != "" {
159+
if client.token.Scheme != "" && client.token.Value != "" {
160160
opts = append([]RequestOpt{OptToken(client.token)}, opts...)
161161
}
162162

163163
return do(client.Client, req, accept, client.strict, out, opts...)
164164
}
165165

166+
// Do a HTTP request and decode it into an object
167+
func (client *Client) Request(req *http.Request, out any, opts ...RequestOpt) error {
168+
client.Mutex.Lock()
169+
defer client.Mutex.Unlock()
170+
171+
// Check rate limit - sleep until next request can be made
172+
now := time.Now()
173+
if !client.ts.IsZero() && client.rate > 0.0 {
174+
next := client.ts.Add(time.Duration(float32(time.Second) / client.rate))
175+
if next.After(now) {
176+
time.Sleep(next.Sub(now))
177+
}
178+
}
179+
180+
// Set timestamp at return
181+
defer func(now time.Time) {
182+
client.ts = now
183+
}(now)
184+
185+
// If client token is set, then add to request, at the beginning so it can be
186+
// overridden by any other options
187+
if client.token.Scheme != "" && client.token.Value != "" {
188+
opts = append([]RequestOpt{OptToken(client.token)}, opts...)
189+
}
190+
191+
return do(client.Client, req, "", false, out, opts...)
192+
}
193+
166194
///////////////////////////////////////////////////////////////////////////////
167195
// PRIVATE METHODS
168196

pkg/client/requestopts.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ func OptPath(value ...string) RequestOpt {
2323
// OptToken adds an authorization header. The header format is "Authorization: Bearer <token>"
2424
func OptToken(value Token) RequestOpt {
2525
return func(r *http.Request) error {
26-
r.Header.Set("Authorization", value.String())
26+
if value.Value != "" {
27+
r.Header.Set("Authorization", value.String())
28+
} else {
29+
r.Header.Del("Authorization")
30+
}
2731
return nil
2832
}
2933
}

pkg/client/transport.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (transport *logtransport) RoundTrip(req *http.Request) (*http.Response, err
6767
fmt.Fprintf(transport.w, " => %v: %q\n", k, v)
6868
}
6969
// If verbose is switched on, read the body
70-
if transport.v && resp.Body != nil && resp.ContentLength > 0 {
70+
if transport.v && resp.Body != nil {
7171
contentType := resp.Header.Get("Content-Type")
7272
if contentType == ContentTypeJson || contentType == ContentTypeTextPlain {
7373
defer resp.Body.Close()

pkg/ipify/client.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,32 @@ func New(opts ...client.ClientOpt) (*Client, error) {
6868
// PUBLIC METHODS
6969

7070
// Get returns the current IP address from the API
71-
func (c *Client) Get() (string, error) {
71+
func (c *Client) Get() (Response, error) {
7272
var response Response
7373
if err := c.Do(nil, &response, client.OptQuery(url.Values{"format": []string{"json"}})); err != nil {
74-
return "", err
74+
return Response{}, err
7575
}
76-
return response.IP, nil
76+
return response, nil
77+
}
78+
79+
///////////////////////////////////////////////////////////////////////////////
80+
// WRITER IMPLEMENTATION
81+
82+
func (Response) Columns() []string {
83+
return []string{"IP"}
84+
}
85+
86+
func (r Response) Count() int {
87+
if r.IP == "" {
88+
return 0
89+
} else {
90+
return 1
91+
}
92+
}
93+
94+
func (r Response) Row(n int) []any {
95+
if n != 0 {
96+
return nil
97+
}
98+
return []any{r.IP}
7799
}

pkg/openai/chat.go

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,114 @@
11
package openai
22

3-
import "github.com/mutablelogic/go-client/pkg/client"
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
// Packages
8+
"github.com/mutablelogic/go-client/pkg/client"
9+
10+
// Namespace imports
11+
. "github.com/djthorpe/go-errors"
12+
)
413

514
///////////////////////////////////////////////////////////////////////////////
615
// SCHEMA
716

8-
type Message struct {
9-
Name string `json:"name,omitempty"`
10-
Role string `json:"role"`
11-
Content string `json:"content"`
17+
type MessageChoice struct {
18+
Index int `json:"index"`
19+
Message Message `json:"message"`
20+
FinishReason string `json:"finish_reason"`
21+
}
22+
23+
type ChatCompletion struct {
24+
Id string `json:"id"`
25+
Object string `json:"object"`
26+
Created int64 `json:"created"`
27+
Model string `json:"model"`
28+
SystemFingerprint string `json:"system_fingerprint"`
29+
Choices []*MessageChoice `json:"choices"`
30+
Usage struct {
31+
PromptTokens int `json:"prompt_tokens"`
32+
CompletionTokens int `json:"completion_tokens"`
33+
TotalTokens int `json:"total_tokens"`
34+
} `json:"usage"`
35+
}
36+
37+
///////////////////////////////////////////////////////////////////////////////
38+
// REQUEST AND RESPONSE
39+
40+
type chatResponseFormat struct {
41+
Type string `json:"type"`
42+
}
43+
44+
type chatRequest struct {
45+
client.Payload `json:"-"`
46+
Model string `json:"model"`
47+
Messages []Message `json:"messages"`
48+
FrequencyPenalty float32 `json:"frequency_penalty,omitempty"`
49+
PresencePenalty float32 `json:"presence_penalty,omitempty"`
50+
MaxTokens int `json:"max_tokens,omitempty"`
51+
Choices int `json:"n,omitempty"`
52+
ResponseFormat *chatResponseFormat `json:"response_format,omitempty"`
53+
Seed int64 `json:"seed,omitempty"`
54+
Stream bool `json:"stream,omitempty"`
55+
Temperature float32 `json:"temperature,omitempty"`
56+
User string `json:"user,omitempty"`
57+
}
58+
59+
func (r chatRequest) Method() string {
60+
return http.MethodPost
61+
}
62+
63+
func (r chatRequest) Type() string {
64+
return client.ContentTypeJson
65+
}
66+
67+
func (r chatRequest) Accept() string {
68+
return client.ContentTypeJson
1269
}
1370

1471
///////////////////////////////////////////////////////////////////////////////
1572
// PUBLIC METHODS
1673

17-
// Return current set of models
18-
func (c *Client) ChatCompletions(model string, messages ...Message) ([]Model, error) {
19-
var request client.Payload
20-
var response modelsResponse
21-
if err := c.Do(request, &response, client.OptPath("models")); err != nil {
22-
return nil, err
74+
// Chat completion response returned by a specific model, based on the provided input messages
75+
func (c *Client) ChatCompletions(model string, messages []Message, opts ...ChatCompletionOpt) (ChatCompletion, error) {
76+
var request chatRequest
77+
var response ChatCompletion
78+
79+
// Check parameters
80+
if model == "" {
81+
return response, ErrBadParameter.With("model")
82+
}
83+
if len(messages) == 0 {
84+
return response, ErrBadParameter.With("messages")
85+
}
86+
87+
// Set request
88+
request.Model = model
89+
request.Messages = messages
90+
91+
// Set chat options
92+
for _, opt := range opts {
93+
if err := opt(&request); err != nil {
94+
return response, err
95+
}
2396
}
24-
return response.Models, nil
97+
98+
// Perform request
99+
if err := c.Do(request, &response, client.OptPath("chat", "completions")); err != nil {
100+
return ChatCompletion{}, err
101+
}
102+
103+
// Return success
104+
return response, nil
105+
}
106+
107+
///////////////////////////////////////////////////////////////////////////////
108+
// UNMARSHALL
109+
110+
func (choice *MessageChoice) UnmarshalJSON(data []byte) error {
111+
// TODO
112+
fmt.Println("UnmarshalJSON", string(data))
113+
return nil
25114
}

pkg/openai/chat_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package openai_test
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"testing"
7+
8+
opts "github.com/mutablelogic/go-client/pkg/client"
9+
openai "github.com/mutablelogic/go-client/pkg/openai"
10+
assert "github.com/stretchr/testify/assert"
11+
)
12+
13+
func Test_chat_001(t *testing.T) {
14+
assert := assert.New(t)
15+
client, err := openai.New(GetApiKey(t), opts.OptTrace(os.Stderr, true))
16+
assert.NoError(err)
17+
assert.NotNil(client)
18+
19+
response, err := client.ChatCompletions("gpt-3.5-turbo", []openai.Message{
20+
openai.SystemMessage("You are a helpful assistant"),
21+
openai.UserMessage("Hello, my name is John. I am a doctor"),
22+
}, openai.OptMaxChoices(1))
23+
assert.NoError(err)
24+
assert.NotNil(response)
25+
26+
data, err := json.MarshalIndent(response, "", " ")
27+
assert.NoError(err)
28+
t.Log(string(data))
29+
}

pkg/openai/embedding.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package openai
2+
3+
import (
4+
"net/http"
5+
6+
// Packages
7+
"github.com/mutablelogic/go-client/pkg/client"
8+
)
9+
10+
///////////////////////////////////////////////////////////////////////////////
11+
// SCHEMA
12+
13+
type Embedding struct {
14+
Object string `json:"object"`
15+
Embedding []float32 `json:"embedding"`
16+
Index int `json:"index"`
17+
}
18+
19+
type EmbeddingResponse struct {
20+
Object string `json:"object"`
21+
Data []Embedding `json:"data"`
22+
Model string `json:"model"`
23+
}
24+
25+
///////////////////////////////////////////////////////////////////////////////
26+
// REQUEST AND RESPONSE
27+
28+
type embeddingRequest struct {
29+
Content string `json:"input"`
30+
Model string `json:"model"`
31+
EncodingFormat string `json:"encoding_format"`
32+
}
33+
34+
func (r embeddingRequest) Method() string {
35+
return http.MethodPost
36+
}
37+
38+
func (r embeddingRequest) Type() string {
39+
return client.ContentTypeJson
40+
}
41+
42+
func (r embeddingRequest) Accept() string {
43+
return client.ContentTypeJson
44+
}

0 commit comments

Comments
 (0)