diff --git a/pkg/loader/loader_test.go b/pkg/loader/loader_test.go index 6377c44e..e46aa823 100644 --- a/pkg/loader/loader_test.go +++ b/pkg/loader/loader_test.go @@ -1,6 +1,99 @@ package loader -import "testing" +import ( + "context" + "encoding/json" + "testing" -func TestLoader(*testing.T) { + "github.com/hexops/autogold/v2" + "github.com/stretchr/testify/require" +) + +func toString(obj any) string { + s, err := json.MarshalIndent(obj, "", " ") + if err != nil { + panic(err) + } + return string(s) +} + +func TestHelloWorld(t *testing.T) { + prg, err := Program(context.Background(), + "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt", + "") + require.NoError(t, err) + autogold.Expect(`{ + "name": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt", + "entryToolId": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt:1", + "toolSet": { + "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/bob.gpt:1": { + "modelName": "gpt-4-turbo-preview", + "internalPrompt": null, + "instructions": "Say hello world", + "id": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/bob.gpt:1", + "localTools": { + "": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/bob.gpt:1" + }, + "source": { + "location": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/bob.gpt", + "lineNo": 1 + }, + "workingDir": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example" + }, + "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt:1": { + "modelName": "gpt-4-turbo-preview", + "internalPrompt": null, + "tools": [ + "../bob.gpt" + ], + "instructions": "call bob", + "id": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt:1", + "toolMapping": { + "../bob.gpt": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/bob.gpt:1" + }, + "localTools": { + "": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt:1" + }, + "source": { + "location": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub/tool.gpt", + "lineNo": 1 + }, + "workingDir": "https://raw.githubusercontent.com/ibuildthecloud/test/bafe5a62174e8a0ea162277dcfe3a2ddb7eea928/example/sub" + } + } +}`).Equal(t, toString(prg)) + + prg, err = Program(context.Background(), "https://get.gptscript.ai/echo.gpt", "") + require.NoError(t, err) + + autogold.Expect(`{ + "name": "https://get.gptscript.ai/echo.gpt", + "entryToolId": "https://get.gptscript.ai/echo.gpt:1", + "toolSet": { + "https://get.gptscript.ai/echo.gpt:1": { + "description": "Returns back the input of the script", + "modelName": "gpt-4-turbo-preview", + "internalPrompt": null, + "arguments": { + "type": "object", + "properties": { + "input": { + "description": " Any string", + "type": "string" + } + } + }, + "instructions": "echo \"${input}\"", + "id": "https://get.gptscript.ai/echo.gpt:1", + "localTools": { + "": "https://get.gptscript.ai/echo.gpt:1" + }, + "source": { + "location": "https://get.gptscript.ai/echo.gpt", + "lineNo": 1 + }, + "workingDir": "https://get.gptscript.ai/" + } + } +}`).Equal(t, toString(prg)) } diff --git a/pkg/loader/url.go b/pkg/loader/url.go index 1762a235..9a6ef19b 100644 --- a/pkg/loader/url.go +++ b/pkg/loader/url.go @@ -27,6 +27,7 @@ func loadURL(ctx context.Context, base *source, name string) (*source, bool, err ) if base.Path != "" && relative { + // Don't use path.Join because this is a URL and will break the :// protocol by cleaning it url = base.Path + "/" + name } @@ -64,7 +65,14 @@ func loadURL(ctx context.Context, base *source, name string) (*source, bool, err pathURL.Path = path.Dir(parsed.Path) pathString := pathURL.String() name = path.Base(parsed.Path) - url = pathString + "/" + name + + // Append to pathString name. This is not the same as the original URL. This is an attempt to end up + // with a clean URL with no ../ in it. + if strings.HasSuffix(pathString, "/") { + url = pathString + name + } else { + url = pathString + "/" + name + } req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil {