Skip to content

Commit ca3948a

Browse files
authored
Merge pull request #3 from mutablelogic/v1
Merged
2 parents b5f7a9a + b16a1ab commit ca3948a

26 files changed

+1350
-46
lines changed

.github/workflows/merge.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
name: "Merge to main"
3+
on:
4+
push:
5+
branches: [ main ]
6+
jobs:
7+
analyze:
8+
name: Analyze
9+
runs-on: ubuntu-latest
10+
permissions:
11+
actions: read
12+
contents: read
13+
security-events: write
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
language: [ 'go' ]
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
- name: Initialize CodeQL
22+
uses: github/codeql-action/init@v2
23+
with:
24+
languages: ${{ matrix.language }}
25+
- name: Autobuild
26+
uses: github/codeql-action/autobuild@v2
27+
- name: Perform CodeQL Analysis
28+
uses: github/codeql-action/analyze@v2
29+
test:
30+
name: Test
31+
runs-on: ubuntu-latest
32+
strategy:
33+
matrix:
34+
go-version: [ '1.19', '1.20', '1.21.x' ]
35+
steps:
36+
- name: Checkout repository
37+
uses: actions/checkout@v4
38+
- name: Setup Go ${{ matrix.go-version }}
39+
uses: actions/setup-go@v4
40+
with:
41+
go-version: ${{ matrix.go-version }}
42+
- name: Run tests
43+
run: make test

.gitignore

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1 @@
1-
# If you prefer the allow list template instead of the deny list, see community template:
2-
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3-
#
4-
# Binaries for programs and plugins
5-
*.exe
6-
*.exe~
7-
*.dll
8-
*.so
9-
*.dylib
10-
11-
# Test binary, built with `go test -c`
12-
*.test
13-
14-
# Output of the go coverage tool, specifically when used with LiteIDE
15-
*.out
16-
17-
# Dependency directories (remove the comment below to include it)
18-
# vendor/
19-
20-
# Go workspace file
21-
go.work
1+
build

build/cli

-5.16 MB
Binary file not shown.

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
}

cmd/cli/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
func main() {
1313
name := path.Base(os.Args[0])
14-
flags, err := NewFlags(name, os.Args[1:], ElevenlabsFlags)
14+
flags, err := NewFlags(name, os.Args[1:], ElevenlabsFlags, OpenAIFlags)
1515
if err != nil {
1616
if err != flag.ErrHelp {
1717
fmt.Fprintln(os.Stderr, err)
@@ -42,6 +42,11 @@ func main() {
4242
fmt.Fprintln(os.Stderr, err)
4343
os.Exit(1)
4444
}
45+
cmd, err = OpenAIRegister(cmd, opts, flags)
46+
if err != nil {
47+
fmt.Fprintln(os.Stderr, err)
48+
os.Exit(1)
49+
}
4550

4651
// Run command
4752
if err := Run(cmd, flags); err != nil {

cmd/cli/openai.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
// Packages
8+
"github.com/mutablelogic/go-client/pkg/client"
9+
"github.com/mutablelogic/go-client/pkg/openai"
10+
)
11+
12+
func OpenAIFlags(flags *Flags) {
13+
flags.String("openai-api-key", "${OPENAI_API_KEY}", "OpenAI API key")
14+
}
15+
16+
func OpenAIRegister(cmd []Client, opts []client.ClientOpt, flags *Flags) ([]Client, error) {
17+
// Get API key
18+
key, err := flags.GetString("openai-api-key")
19+
if err != nil {
20+
return nil, err
21+
}
22+
23+
// Create client
24+
openai, err := openai.New(key, opts...)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
// Register commands
30+
cmd = append(cmd, Client{
31+
ns: "openai",
32+
cmd: []Command{
33+
{Name: "models", Description: "Return registered models", MinArgs: 2, MaxArgs: 2, Fn: OpenAIModels(openai, flags)},
34+
{Name: "model", Description: "Return model information", MinArgs: 3, MaxArgs: 3, Fn: OpenAIModel(openai, flags)},
35+
},
36+
})
37+
38+
// Return success
39+
return cmd, nil
40+
}
41+
42+
func OpenAIModels(client *openai.Client, flags *Flags) CommandFn {
43+
return func() error {
44+
if models, err := client.Models(); err != nil {
45+
return err
46+
} else if data, err := json.MarshalIndent(models, "", " "); err != nil {
47+
return err
48+
} else {
49+
fmt.Printf("Models: %s\n", data)
50+
}
51+
return nil
52+
}
53+
}
54+
55+
func OpenAIModel(client *openai.Client, flags *Flags) CommandFn {
56+
return func() error {
57+
if model, err := client.Model(flags.Arg(2)); err != nil {
58+
return err
59+
} else if data, err := json.MarshalIndent(model, "", " "); err != nil {
60+
return err
61+
} else {
62+
fmt.Printf("%s\n", data)
63+
}
64+
return nil
65+
}
66+
}

pkg/client/client.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const (
5858
ContentTypeTextPlain = "text/plain"
5959
ContentTypeTextHTML = "text/html"
6060
ContentTypeBinary = "application/octet-stream"
61+
ContentTypeForm = "multipart/form-data"
6162
)
6263

6364
///////////////////////////////////////////////////////////////////////////////
@@ -156,13 +157,41 @@ func (client *Client) Do(in Payload, out any, opts ...RequestOpt) error {
156157
}
157158

158159
// If client token is set, then add to request
159-
if client.token.Scheme != "" {
160+
if client.token.Scheme != "" && client.token.Value != "" {
160161
opts = append([]RequestOpt{OptToken(client.token)}, opts...)
161162
}
162163

163164
return do(client.Client, req, accept, client.strict, out, opts...)
164165
}
165166

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

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()

0 commit comments

Comments
 (0)