diff --git a/gptscript.go b/gptscript.go index d32a616..cc0d620 100644 --- a/gptscript.go +++ b/gptscript.go @@ -181,8 +181,8 @@ func (g *GPTScript) Parse(ctx context.Context, fileName string, opts ...ParseOpt return doc.Nodes, nil } -// ParseTool will parse the given string into a tool. -func (g *GPTScript) ParseTool(ctx context.Context, toolDef string) ([]Node, error) { +// ParseContent will parse the given string into a tool. +func (g *GPTScript) ParseContent(ctx context.Context, toolDef string) ([]Node, error) { out, err := g.runBasicCommand(ctx, "parse", map[string]any{"content": toolDef}) if err != nil { return nil, err @@ -214,6 +214,53 @@ func (g *GPTScript) Fmt(ctx context.Context, nodes []Node) (string, error) { return out, nil } +type LoadOptions struct { + DisableCache bool + SubTool string +} + +// LoadFile will load the given file into a Program. +func (g *GPTScript) LoadFile(ctx context.Context, fileName string, opts ...LoadOptions) (*Program, error) { + return g.load(ctx, map[string]any{"file": fileName}, opts...) +} + +// LoadContent will load the given content into a Program. +func (g *GPTScript) LoadContent(ctx context.Context, content string, opts ...LoadOptions) (*Program, error) { + return g.load(ctx, map[string]any{"content": content}, opts...) +} + +// LoadTools will load the given tools into a Program. +func (g *GPTScript) LoadTools(ctx context.Context, toolDefs []ToolDef, opts ...LoadOptions) (*Program, error) { + return g.load(ctx, map[string]any{"toolDefs": toolDefs}, opts...) +} + +func (g *GPTScript) load(ctx context.Context, payload map[string]any, opts ...LoadOptions) (*Program, error) { + for _, opt := range opts { + if opt.DisableCache { + payload["disableCache"] = true + } + if opt.SubTool != "" { + payload["subTool"] = opt.SubTool + } + } + + out, err := g.runBasicCommand(ctx, "load", payload) + if err != nil { + return nil, err + } + + type loadResponse struct { + Program *Program `json:"program"` + } + + prg := new(loadResponse) + if err = json.Unmarshal([]byte(out), prg); err != nil { + return nil, err + } + + return prg.Program, nil +} + // Version will return the output of `gptscript --version` func (g *GPTScript) Version(ctx context.Context) (string, error) { out, err := g.runBasicCommand(ctx, "version", nil) diff --git a/gptscript_test.go b/gptscript_test.go index 2c18c47..f298cab 100644 --- a/gptscript_test.go +++ b/gptscript_test.go @@ -498,7 +498,7 @@ func TestParseFileWithMetadata(t *testing.T) { } func TestParseTool(t *testing.T) { - tools, err := g.ParseTool(context.Background(), "echo hello") + tools, err := g.ParseContent(context.Background(), "echo hello") if err != nil { t.Errorf("Error parsing tool: %v", err) } @@ -517,7 +517,7 @@ func TestParseTool(t *testing.T) { } func TestEmptyParseTool(t *testing.T) { - tools, err := g.ParseTool(context.Background(), "") + tools, err := g.ParseContent(context.Background(), "") if err != nil { t.Errorf("Error parsing tool: %v", err) } @@ -528,7 +528,7 @@ func TestEmptyParseTool(t *testing.T) { } func TestParseToolWithTextNode(t *testing.T) { - tools, err := g.ParseTool(context.Background(), "echo hello\n---\n!markdown\nhello") + tools, err := g.ParseContent(context.Background(), "echo hello\n---\n!markdown\nhello") if err != nil { t.Errorf("Error parsing tool: %v", err) } @@ -735,8 +735,8 @@ func TestFileChat(t *testing.T) { } inputs := []string{ "List the 3 largest of the Great Lakes by volume.", - "What is the volume of the second in the list in cubic miles?", - "What is the total area of the third in the list in square miles?", + "For the second one in the list: what is the volume cubic miles?", + "For the third one in the list: what is the total area in square miles?", } expectedOutputs := []string{ @@ -1220,3 +1220,115 @@ func TestParseThenEvaluateWithMetadata(t *testing.T) { t.Errorf("Unexpected output: %s", out) } } + +func TestLoadFile(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("Error getting working directory: %v", err) + } + + prg, err := g.LoadFile(context.Background(), wd+"/test/global-tools.gpt") + if err != nil { + t.Fatalf("Error loading file: %v", err) + } + + if prg.EntryToolID == "" { + t.Errorf("Unexpected entry tool ID: %s", prg.EntryToolID) + } + + if len(prg.ToolSet) == 0 { + t.Errorf("Unexpected number of tools: %d", len(prg.ToolSet)) + } + + if prg.Name == "" { + t.Errorf("Unexpected name: %s", prg.Name) + } +} + +func TestLoadRemoteFile(t *testing.T) { + prg, err := g.LoadFile(context.Background(), "github.com/gptscript-ai/context/workspace") + if err != nil { + t.Fatalf("Error loading file: %v", err) + } + + if prg.EntryToolID == "" { + t.Errorf("Unexpected entry tool ID: %s", prg.EntryToolID) + } + + if len(prg.ToolSet) == 0 { + t.Errorf("Unexpected number of tools: %d", len(prg.ToolSet)) + } + + if prg.Name == "" { + t.Errorf("Unexpected name: %s", prg.Name) + } +} + +func TestLoadContent(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("Error getting working directory: %v", err) + } + + content, err := os.ReadFile(wd + "/test/global-tools.gpt") + if err != nil { + t.Fatalf("Error reading file: %v", err) + } + + prg, err := g.LoadContent(context.Background(), string(content)) + if err != nil { + t.Fatalf("Error loading file: %v", err) + } + + if prg.EntryToolID == "" { + t.Errorf("Unexpected entry tool ID: %s", prg.EntryToolID) + } + + if len(prg.ToolSet) == 0 { + t.Errorf("Unexpected number of tools: %d", len(prg.ToolSet)) + } + + // Name won't be set in this case + if prg.Name != "" { + t.Errorf("Unexpected name: %s", prg.Name) + } +} + +func TestLoadTools(t *testing.T) { + tools := []ToolDef{ + { + Tools: []string{"echo"}, + Instructions: "echo 'hello there'", + }, + { + Name: "other", + Tools: []string{"echo"}, + Instructions: "echo 'hello somewhere else'", + }, + { + Name: "echo", + Tools: []string{"sys.exec"}, + Description: "Echoes the input", + Arguments: ObjectSchema("input", "The string input to echo"), + Instructions: "#!/bin/bash\n echo ${input}", + }, + } + + prg, err := g.LoadTools(context.Background(), tools) + if err != nil { + t.Fatalf("Error loading file: %v", err) + } + + if prg.EntryToolID == "" { + t.Errorf("Unexpected entry tool ID: %s", prg.EntryToolID) + } + + if len(prg.ToolSet) == 0 { + t.Errorf("Unexpected number of tools: %d", len(prg.ToolSet)) + } + + // Name won't be set in this case + if prg.Name != "" { + t.Errorf("Unexpected name: %s", prg.Name) + } +}