Skip to content

Commit b08b1bc

Browse files
chore: cache github and http lookups in loader
1 parent 1bf1f8b commit b08b1bc

File tree

16 files changed

+262
-125
lines changed

16 files changed

+262
-125
lines changed

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ build-ui:
99
touch static/ui/placeholder static/ui/_nuxt/_placeholder
1010
cp -rp ui/.output/public/* static/ui/
1111

12+
build-exe:
13+
GOOS=windows go build -o bin/gptscript.exe -tags "${GO_TAGS}" .
14+
1215
build:
1316
CGO_ENABLED=0 go build -o bin/gptscript -tags "${GO_TAGS}" -ldflags "-s -w" .
1417

pkg/cache/cache.go

+60-10
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ package cache
22

33
import (
44
"context"
5+
"crypto/sha256"
6+
"encoding/gob"
7+
"encoding/hex"
58
"errors"
69
"io/fs"
710
"os"
811
"path/filepath"
912

1013
"github.com/adrg/xdg"
14+
"github.com/getkin/kin-openapi/openapi3"
15+
openai "github.com/gptscript-ai/chat-completion-client"
1116
"github.com/gptscript-ai/gptscript/pkg/types"
1217
"github.com/gptscript-ai/gptscript/pkg/version"
1318
)
@@ -22,6 +27,11 @@ type Options struct {
2227
CacheDir string `usage:"Directory to store cache (default: $XDG_CACHE_HOME/gptscript)"`
2328
}
2429

30+
func init() {
31+
gob.Register(openai.ChatCompletionRequest{})
32+
gob.Register(openapi3.Schema{})
33+
}
34+
2535
func Complete(opts ...Options) (result Options) {
2636
for _, opt := range opts {
2737
result.CacheDir = types.FirstSet(opt.CacheDir, result.CacheDir)
@@ -59,22 +69,62 @@ func (c *Client) CacheDir() string {
5969
return c.dir
6070
}
6171

62-
func (c *Client) Store(key string, content []byte) error {
63-
if c == nil || c.noop {
72+
func (c *Client) cacheKey(key any) (string, error) {
73+
hash := sha256.New()
74+
if err := gob.NewEncoder(hash).Encode(key); err != nil {
75+
return "", err
76+
}
77+
digest := hash.Sum(nil)
78+
return hex.EncodeToString(digest), nil
79+
}
80+
81+
func (c *Client) Store(ctx context.Context, key, value any) error {
82+
if c == nil {
6483
return nil
6584
}
66-
return os.WriteFile(filepath.Join(c.dir, key), content, 0644)
85+
86+
if c.noop || IsNoCache(ctx) {
87+
keyValue, err := c.cacheKey(key)
88+
if err == nil {
89+
p := filepath.Join(c.dir, keyValue)
90+
if _, err := os.Stat(p); err == nil {
91+
_ = os.Remove(p)
92+
}
93+
}
94+
return nil
95+
}
96+
97+
keyValue, err := c.cacheKey(key)
98+
if err != nil {
99+
return err
100+
}
101+
102+
f, err := os.Create(filepath.Join(c.dir, keyValue))
103+
if err != nil {
104+
return err
105+
}
106+
defer f.Close()
107+
108+
return gob.NewEncoder(f).Encode(value)
67109
}
68110

69-
func (c *Client) Get(key string) ([]byte, bool, error) {
70-
if c == nil || c.noop {
71-
return nil, false, nil
111+
func (c *Client) Get(ctx context.Context, key, out any) (bool, error) {
112+
if c == nil || c.noop || IsNoCache(ctx) {
113+
return false, nil
72114
}
73-
data, err := os.ReadFile(filepath.Join(c.dir, key))
115+
116+
keyValue, err := c.cacheKey(key)
117+
if err != nil {
118+
return false, err
119+
}
120+
121+
f, err := os.Open(filepath.Join(c.dir, keyValue))
74122
if errors.Is(err, fs.ErrNotExist) {
75-
return nil, false, nil
123+
return false, nil
76124
} else if err != nil {
77-
return nil, false, err
125+
return false, err
78126
}
79-
return data, true, nil
127+
defer f.Close()
128+
129+
return gob.NewDecoder(f).Decode(out) == nil, nil
80130
}

pkg/cli/eval.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,19 @@ func (e *Eval) Run(cmd *cobra.Command, args []string) error {
4949
tool.Temperature = &temp32
5050
}
5151

52-
prg, err := loader.ProgramFromSource(cmd.Context(), tool.String(), "")
52+
opts, err := e.gptscript.NewGPTScriptOpts()
5353
if err != nil {
5454
return err
5555
}
5656

57-
opts, err := e.gptscript.NewGPTScriptOpts()
57+
runner, err := gptscript.New(&opts)
5858
if err != nil {
5959
return err
6060
}
6161

62-
runner, err := gptscript.New(&opts)
62+
prg, err := loader.ProgramFromSource(cmd.Context(), tool.String(), "", loader.Options{
63+
Cache: runner.Cache,
64+
})
6365
if err != nil {
6466
return err
6567
}

pkg/cli/gptscript.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func (r *GPTScript) listModels(ctx context.Context, gptScript *gptscript.GPTScri
259259
return nil
260260
}
261261

262-
func (r *GPTScript) readProgram(ctx context.Context, args []string) (prg types.Program, err error) {
262+
func (r *GPTScript) readProgram(ctx context.Context, runner *gptscript.GPTScript, args []string) (prg types.Program, err error) {
263263
if len(args) == 0 {
264264
return
265265
}
@@ -278,10 +278,14 @@ func (r *GPTScript) readProgram(ctx context.Context, args []string) (prg types.P
278278
}
279279
r.readData = data
280280
}
281-
return loader.ProgramFromSource(ctx, string(data), r.SubTool)
281+
return loader.ProgramFromSource(ctx, string(data), r.SubTool, loader.Options{
282+
Cache: runner.Cache,
283+
})
282284
}
283285

284-
return loader.Program(ctx, args[0], r.SubTool)
286+
return loader.Program(ctx, args[0], r.SubTool, loader.Options{
287+
Cache: runner.Cache,
288+
})
285289
}
286290

287291
func (r *GPTScript) PrintOutput(toolInput, toolOutput string) (err error) {
@@ -337,7 +341,7 @@ func (r *GPTScript) Run(cmd *cobra.Command, args []string) (retErr error) {
337341
return r.listModels(ctx, gptScript, args)
338342
}
339343

340-
prg, err := r.readProgram(ctx, args)
344+
prg, err := r.readProgram(ctx, gptScript, args)
341345
if err != nil {
342346
return err
343347
}
@@ -392,7 +396,7 @@ func (r *GPTScript) Run(cmd *cobra.Command, args []string) (retErr error) {
392396

393397
if prg.IsChat() || r.ForceChat {
394398
return chat.Start(r.NewRunContext(cmd), nil, gptScript, func() (types.Program, error) {
395-
return r.readProgram(ctx, args)
399+
return r.readProgram(ctx, gptScript, args)
396400
}, os.Environ(), toolInput)
397401
}
398402

pkg/debugcmd/debug.go

+14
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ type WrappedCmd struct {
1616
Dir string
1717
}
1818

19+
func (w *WrappedCmd) Stdout() string {
20+
return w.r.Stdout()
21+
}
22+
1923
func (w *WrappedCmd) Run() error {
2024
if len(w.Env) > 0 {
2125
w.c.Env = w.Env
@@ -51,6 +55,16 @@ type recorder struct {
5155
entries []entry
5256
}
5357

58+
func (r *recorder) Stdout() string {
59+
buf := strings.Builder{}
60+
for _, e := range r.entries {
61+
if !e.err {
62+
buf.Write(e.data)
63+
}
64+
}
65+
return buf.String()
66+
}
67+
5468
func (r *recorder) dump() string {
5569
var errMessage strings.Builder
5670
for _, entry := range r.entries {

pkg/gptscript/gptscript.go

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var log = mvl.Package()
2424
type GPTScript struct {
2525
Registry *llm.Registry
2626
Runner *runner.Runner
27+
Cache *cache.Client
2728
WorkspacePath string
2829
DeleteWorkspaceOnClose bool
2930
}
@@ -99,6 +100,7 @@ func New(opts *Options) (*GPTScript, error) {
99100
return &GPTScript{
100101
Registry: registry,
101102
Runner: runner,
103+
Cache: cacheClient,
102104
WorkspacePath: opts.Workspace,
103105
DeleteWorkspaceOnClose: opts.Workspace == "",
104106
}, nil

pkg/hash/seed.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package hash
22

3-
import "hash/fnv"
3+
import (
4+
"encoding/gob"
5+
"hash/fnv"
6+
)
47

58
func Seed(input any) int {
6-
s := Encode(input)
79
h := fnv.New32a()
8-
_, _ = h.Write([]byte(s))
10+
if err := gob.NewEncoder(h).Encode(input); err != nil {
11+
panic(err)
12+
}
913
return int(h.Sum32())
1014
}

pkg/hash/sha256.go

+4-26
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package hash
22

33
import (
44
"crypto/sha256"
5+
"encoding/gob"
56
"encoding/hex"
6-
"encoding/json"
77
)
88

99
func ID(parts ...string) string {
@@ -19,31 +19,9 @@ func ID(parts ...string) string {
1919
}
2020

2121
func Digest(obj any) string {
22-
data, err := json.Marshal(obj)
23-
if err != nil {
22+
hash := sha256.New()
23+
if err := gob.NewEncoder(hash).Encode(obj); err != nil {
2424
panic(err)
2525
}
26-
27-
hash := sha256.Sum256(data)
28-
return hex.EncodeToString(hash[:])
29-
}
30-
31-
func Encode(obj any) string {
32-
data, err := json.Marshal(obj)
33-
if err != nil {
34-
panic(err)
35-
}
36-
37-
asMap := map[string]any{}
38-
if err := json.Unmarshal(data, &asMap); err != nil {
39-
panic(err)
40-
}
41-
42-
data, err = json.Marshal(asMap)
43-
if err != nil {
44-
panic(err)
45-
}
46-
47-
hash := sha256.Sum256(data)
48-
return hex.EncodeToString(hash[:])
26+
return hex.EncodeToString(hash.Sum(nil))
4927
}

pkg/loader/github/github.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package github
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"io"
@@ -9,7 +10,9 @@ import (
910
"path/filepath"
1011
"strings"
1112

13+
"github.com/gptscript-ai/gptscript/pkg/cache"
1214
"github.com/gptscript-ai/gptscript/pkg/loader"
15+
"github.com/gptscript-ai/gptscript/pkg/repos/git"
1316
"github.com/gptscript-ai/gptscript/pkg/system"
1417
"github.com/gptscript-ai/gptscript/pkg/types"
1518
)
@@ -29,11 +32,14 @@ func init() {
2932
loader.AddVSC(Load)
3033
}
3134

32-
func getCommit(account, repo, ref string) (string, error) {
33-
url := fmt.Sprintf(githubCommitURL, account, repo, ref)
34-
client := &http.Client{}
35+
func getCommitLsRemote(ctx context.Context, account, repo, ref string) (string, error) {
36+
url := fmt.Sprintf(githubRepoURL, account, repo)
37+
return git.LsRemote(ctx, url, ref)
38+
}
3539

36-
req, err := http.NewRequest(http.MethodGet, url, nil)
40+
func getCommit(ctx context.Context, account, repo, ref string) (string, error) {
41+
url := fmt.Sprintf(githubCommitURL, account, repo, ref)
42+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
3743
if err != nil {
3844
return "", fmt.Errorf("failed to create request of %s/%s at %s: %w", account, repo, url, err)
3945
}
@@ -42,13 +48,15 @@ func getCommit(account, repo, ref string) (string, error) {
4248
req.Header.Add("Authorization", "Bearer "+githubAuthToken)
4349
}
4450

45-
resp, err := client.Do(req)
46-
51+
resp, err := http.DefaultClient.Do(req)
4752
if err != nil {
4853
return "", err
4954
} else if resp.StatusCode != http.StatusOK {
5055
c, _ := io.ReadAll(resp.Body)
5156
resp.Body.Close()
57+
if commit, err := getCommitLsRemote(ctx, account, repo, ref); err == nil {
58+
return commit, nil
59+
}
5260
return "", fmt.Errorf("failed to get GitHub commit of %s/%s at %s: %s %s",
5361
account, repo, ref, resp.Status, c)
5462
}
@@ -68,7 +76,7 @@ func getCommit(account, repo, ref string) (string, error) {
6876
return commit.SHA, nil
6977
}
7078

71-
func Load(urlName string) (string, *types.Repo, bool, error) {
79+
func Load(ctx context.Context, _ *cache.Client, urlName string) (string, *types.Repo, bool, error) {
7280
if !strings.HasPrefix(urlName, GithubPrefix) {
7381
return "", nil, false, nil
7482
}
@@ -93,7 +101,7 @@ func Load(urlName string) (string, *types.Repo, bool, error) {
93101
path += "/tool.gpt"
94102
}
95103

96-
ref, err := getCommit(account, repo, ref)
104+
ref, err := getCommit(ctx, account, repo, ref)
97105
if err != nil {
98106
return "", nil, false, err
99107
}

0 commit comments

Comments
 (0)