Skip to content

Commit

Permalink
feat: add load API
Browse files Browse the repository at this point in the history
This change also changes ParseTool to ParseContent for consistency.

Signed-off-by: Donnie Adams <[email protected]>
  • Loading branch information
thedadams committed Aug 14, 2024
1 parent ef58327 commit e0876d7
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 7 deletions.
51 changes: 49 additions & 2 deletions gptscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
122 changes: 117 additions & 5 deletions gptscript_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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)
}
}

0 comments on commit e0876d7

Please sign in to comment.