From c624844299f927319ae9136ae5ba4136bba43828 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Tue, 4 Jun 2024 15:30:58 -0700 Subject: [PATCH] chore: look for agent.gpt and then tool.gpt when referencing folders --- pkg/loader/github/github.go | 41 +++++++++++++----- pkg/loader/github/github_test.go | 46 ++++++++++++++++++++ pkg/loader/loader.go | 9 ++-- pkg/loader/loader_test.go | 74 ++++++++++++++++++++++++++++++++ pkg/types/tool.go | 4 ++ 5 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 pkg/loader/github/github_test.go diff --git a/pkg/loader/github/github.go b/pkg/loader/github/github.go index 3c141246..1ca2f6c4 100644 --- a/pkg/loader/github/github.go +++ b/pkg/loader/github/github.go @@ -7,7 +7,7 @@ import ( "io" "net/http" "os" - "path/filepath" + gpath "path" "regexp" "strings" @@ -15,7 +15,6 @@ import ( "github.com/gptscript-ai/gptscript/pkg/loader" "github.com/gptscript-ai/gptscript/pkg/mvl" "github.com/gptscript-ai/gptscript/pkg/repos/git" - "github.com/gptscript-ai/gptscript/pkg/system" "github.com/gptscript-ai/gptscript/pkg/types" ) @@ -108,23 +107,45 @@ func Load(ctx context.Context, _ *cache.Client, urlName string) (string, *types. account, repo := parts[1], parts[2] path := strings.Join(parts[3:], "/") - if path == "" || path == "/" { - path = "tool.gpt" - } else if !strings.HasSuffix(path, system.Suffix) && !strings.Contains(parts[len(parts)-1], ".") { - path += "/tool.gpt" - } - ref, err := getCommit(ctx, account, repo, ref) if err != nil { return "", nil, false, err } downloadURL := fmt.Sprintf(githubDownloadURL, account, repo, ref, path) + if path == "" || path == "/" || !strings.Contains(parts[len(parts)-1], ".") { + var ( + testPath string + testURL string + ) + for i, ext := range types.DefaultFiles { + if strings.HasSuffix(path, "/") { + testPath = path + ext + } else { + testPath = path + "/" + ext + } + testURL = fmt.Sprintf(githubDownloadURL, account, repo, ref, testPath) + if i == len(types.DefaultFiles)-1 { + // no reason to test the last one, we are just going to use it. Being that the default list is only + // two elements this loop could have been one check, but hey over-engineered code ftw. + break + } + if resp, err := http.Head(testURL); err == nil { + _ = resp.Body.Close() + if resp.StatusCode == 200 { + break + } + } + } + downloadURL = testURL + path = testPath + } + return downloadURL, &types.Repo{ VCS: "git", Root: fmt.Sprintf(githubRepoURL, account, repo), - Path: filepath.Dir(path), - Name: filepath.Base(path), + Path: gpath.Dir(path), + Name: gpath.Base(path), Revision: ref, }, true, nil } diff --git a/pkg/loader/github/github_test.go b/pkg/loader/github/github_test.go new file mode 100644 index 00000000..169f2e7e --- /dev/null +++ b/pkg/loader/github/github_test.go @@ -0,0 +1,46 @@ +package github + +import ( + "context" + "testing" + + "github.com/gptscript-ai/gptscript/pkg/types" + "github.com/hexops/autogold/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoad(t *testing.T) { + url, repo, ok, err := Load(context.Background(), nil, "github.com/gptscript-ai/gptscript/pkg/loader/testdata/tool@172dfb0") + require.NoError(t, err) + assert.True(t, ok) + autogold.Expect("https://raw.githubusercontent.com/gptscript-ai/gptscript/172dfb00b48c6adbbaa7e99270933f95887d1b91/pkg/loader/testdata/tool/tool.gpt").Equal(t, url) + autogold.Expect(&types.Repo{ + VCS: "git", Root: "https://github.com/gptscript-ai/gptscript.git", + Path: "pkg/loader/testdata/tool", + Name: "tool.gpt", + Revision: "172dfb00b48c6adbbaa7e99270933f95887d1b91", + }).Equal(t, repo) + + url, repo, ok, err = Load(context.Background(), nil, "github.com/gptscript-ai/gptscript/pkg/loader/testdata/agent@172dfb0") + require.NoError(t, err) + assert.True(t, ok) + autogold.Expect("https://raw.githubusercontent.com/gptscript-ai/gptscript/172dfb00b48c6adbbaa7e99270933f95887d1b91/pkg/loader/testdata/agent/agent.gpt").Equal(t, url) + autogold.Expect(&types.Repo{ + VCS: "git", Root: "https://github.com/gptscript-ai/gptscript.git", + Path: "pkg/loader/testdata/agent", + Name: "agent.gpt", + Revision: "172dfb00b48c6adbbaa7e99270933f95887d1b91", + }).Equal(t, repo) + + url, repo, ok, err = Load(context.Background(), nil, "github.com/gptscript-ai/gptscript/pkg/loader/testdata/bothtoolagent@172dfb0") + require.NoError(t, err) + assert.True(t, ok) + autogold.Expect("https://raw.githubusercontent.com/gptscript-ai/gptscript/172dfb00b48c6adbbaa7e99270933f95887d1b91/pkg/loader/testdata/bothtoolagent/agent.gpt").Equal(t, url) + autogold.Expect(&types.Repo{ + VCS: "git", Root: "https://github.com/gptscript-ai/gptscript.git", + Path: "pkg/loader/testdata/bothtoolagent", + Name: "agent.gpt", + Revision: "172dfb00b48c6adbbaa7e99270933f95887d1b91", + }).Equal(t, repo) +} diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index 76ae0323..7d1142a2 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -75,9 +75,12 @@ func loadLocal(base *source, name string) (*source, bool, error) { filePath := path.Join(base.Path, name) if s, err := os.Stat(filepath.Clean(filePath)); err == nil && s.IsDir() { - toolPath := path.Join(filePath, "tool.gpt") - if s, err := os.Stat(filepath.Clean(toolPath)); err == nil && !s.IsDir() { - filePath = toolPath + for _, def := range types.DefaultFiles { + toolPath := path.Join(filePath, def) + if s, err := os.Stat(filepath.Clean(toolPath)); err == nil && !s.IsDir() { + filePath = toolPath + break + } } } diff --git a/pkg/loader/loader_test.go b/pkg/loader/loader_test.go index 0ae6069e..a5c328b5 100644 --- a/pkg/loader/loader_test.go +++ b/pkg/loader/loader_test.go @@ -194,3 +194,77 @@ func TestHelloWorld(t *testing.T) { } }`).Equal(t, toString(prg)) } + +func TestDefault(t *testing.T) { + prg, err := Program(context.Background(), "./testdata/tool", "") + require.NoError(t, err) + autogold.Expect(`{ + "name": "./testdata/tool", + "entryToolId": "testdata/tool/tool.gpt:tool", + "toolSet": { + "testdata/tool/tool.gpt:tool": { + "name": "tool", + "modelName": "gpt-4o", + "internalPrompt": null, + "instructions": "a tool", + "id": "testdata/tool/tool.gpt:tool", + "localTools": { + "tool": "testdata/tool/tool.gpt:tool" + }, + "source": { + "location": "testdata/tool/tool.gpt", + "lineNo": 1 + }, + "workingDir": "testdata/tool" + } + } +}`).Equal(t, toString(prg)) + + prg, err = Program(context.Background(), "./testdata/agent", "") + require.NoError(t, err) + autogold.Expect(`{ + "name": "./testdata/agent", + "entryToolId": "testdata/agent/agent.gpt:agent", + "toolSet": { + "testdata/agent/agent.gpt:agent": { + "name": "agent", + "modelName": "gpt-4o", + "internalPrompt": null, + "instructions": "an agent", + "id": "testdata/agent/agent.gpt:agent", + "localTools": { + "agent": "testdata/agent/agent.gpt:agent" + }, + "source": { + "location": "testdata/agent/agent.gpt", + "lineNo": 1 + }, + "workingDir": "testdata/agent" + } + } +}`).Equal(t, toString(prg)) + + prg, err = Program(context.Background(), "./testdata/bothtoolagent", "") + require.NoError(t, err) + autogold.Expect(`{ + "name": "./testdata/bothtoolagent", + "entryToolId": "testdata/bothtoolagent/agent.gpt:agent", + "toolSet": { + "testdata/bothtoolagent/agent.gpt:agent": { + "name": "agent", + "modelName": "gpt-4o", + "internalPrompt": null, + "instructions": "an agent", + "id": "testdata/bothtoolagent/agent.gpt:agent", + "localTools": { + "agent": "testdata/bothtoolagent/agent.gpt:agent" + }, + "source": { + "location": "testdata/bothtoolagent/agent.gpt", + "lineNo": 1 + }, + "workingDir": "testdata/bothtoolagent" + } + } +}`).Equal(t, toString(prg)) +} diff --git a/pkg/types/tool.go b/pkg/types/tool.go index b93b0eb6..4163a2e7 100644 --- a/pkg/types/tool.go +++ b/pkg/types/tool.go @@ -20,6 +20,10 @@ const ( CommandPrefix = "#!" ) +var ( + DefaultFiles = []string{"agent.gpt", "tool.gpt"} +) + type ErrToolNotFound struct { ToolName string }