Skip to content

Commit 3871ee6

Browse files
Add sys.read and sys.write
1 parent 64be97b commit 3871ee6

File tree

6 files changed

+110
-2
lines changed

6 files changed

+110
-2
lines changed

pkg/builtin/builtin.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package builtin
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"unicode/utf8"
10+
11+
"github.com/acorn-io/gptscript/pkg/types"
12+
)
13+
14+
var Tools = map[string]types.Tool{
15+
"sys.read": {
16+
Description: "Reads the contents of a file",
17+
Arguments: types.ObjectSchema(
18+
"filename", "The name of the file to read"),
19+
BuiltinFunc: func(ctx context.Context, env []string, input string) (string, error) {
20+
var params struct {
21+
Filename string `json:"filename,omitempty"`
22+
}
23+
if err := json.Unmarshal([]byte(input), &params); err != nil {
24+
return "", err
25+
}
26+
27+
log.Debugf("Reading file %s", params.Filename)
28+
data, err := os.ReadFile(params.Filename)
29+
if err != nil {
30+
return "", err
31+
}
32+
33+
if utf8.Valid(data) {
34+
return string(data), nil
35+
}
36+
return base64.StdEncoding.EncodeToString(data), nil
37+
},
38+
},
39+
"sys.write": {
40+
Description: "Write the contents to a file",
41+
Arguments: types.ObjectSchema(
42+
"filename", "The name of the file to write to",
43+
"content", "The content to write"),
44+
BuiltinFunc: func(ctx context.Context, env []string, input string) (string, error) {
45+
var params struct {
46+
Filename string `json:"filename,omitempty"`
47+
Content string `json:"content,omitempty"`
48+
}
49+
if err := json.Unmarshal([]byte(input), &params); err != nil {
50+
return "", err
51+
}
52+
53+
data := []byte(params.Content)
54+
msg := fmt.Sprintf("Wrote %d bytes to file %s", len(data), params.Filename)
55+
log.Debugf(msg)
56+
57+
return "", os.WriteFile(params.Filename, data, 0644)
58+
},
59+
},
60+
}
61+
62+
func Builtin(name string) (types.Tool, bool) {
63+
t, ok := Tools[name]
64+
t.Name = name
65+
t.ID = name
66+
t.Instructions = "#!" + name
67+
return t, ok
68+
}

pkg/builtin/log.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package builtin
2+
3+
import "github.com/acorn-io/gptscript/pkg/mvl"
4+
5+
var log = mvl.Package()

pkg/engine/engine.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ func (c *Context) getTool(name string) (types.Tool, error) {
137137
return tool, nil
138138
}
139139

140-
func (e *Engine) runCommand(tool types.Tool, input string) (string, error) {
140+
func (e *Engine) runCommand(ctx context.Context, tool types.Tool, input string) (string, error) {
141+
if tool.BuiltinFunc != nil {
142+
return tool.BuiltinFunc(ctx, e.Env, input)
143+
}
144+
141145
env := e.Env[:]
142146
data := map[string]any{}
143147

@@ -197,7 +201,7 @@ func (e *Engine) Start(ctx Context, input string) (*Return, error) {
197201
tool := ctx.Tool
198202

199203
if tool.IsCommand() {
200-
s, err := e.runCommand(tool, input)
204+
s, err := e.runCommand(ctx.Ctx, tool, input)
201205
if err != nil {
202206
return nil, err
203207
}

pkg/loader/loader.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"strings"
2020

2121
"github.com/acorn-io/gptscript/pkg/assemble"
22+
"github.com/acorn-io/gptscript/pkg/builtin"
2223
"github.com/acorn-io/gptscript/pkg/parser"
2324
"github.com/acorn-io/gptscript/pkg/types"
2425
)
@@ -332,6 +333,14 @@ func Program(ctx context.Context, name, subToolName string) (types.Program, erro
332333
}
333334

334335
func Resolve(ctx context.Context, prg *types.Program, base *Source, name, subTool string) (types.Tool, error) {
336+
if subTool == "" {
337+
t, ok := builtin.Builtin(name)
338+
if ok {
339+
prg.ToolSet[t.ID] = t
340+
return t, nil
341+
}
342+
}
343+
335344
s, err := Input(ctx, base, name)
336345
if err != nil {
337346
return types.Tool{}, err

pkg/types/jsonschema.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ type JSONSchema struct {
1616
AdditionalProperties bool `json:"additionalProperties,omitempty"`
1717
}
1818

19+
func ObjectSchema(kv ...string) *JSONSchema {
20+
s := &JSONSchema{
21+
Property: Property{
22+
Type: "object",
23+
},
24+
Properties: map[string]Property{},
25+
}
26+
for i, v := range kv {
27+
if i%2 == 1 {
28+
s.Properties[kv[i-1]] = Property{
29+
Description: v,
30+
Type: "string",
31+
}
32+
}
33+
}
34+
return s
35+
}
36+
1937
type Property struct {
2038
Description string `json:"description,omitempty"`
2139
Type string `json:"type,omitempty"`

pkg/types/tool.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package types
22

33
import (
4+
"context"
45
"fmt"
56
"strings"
67
)
@@ -12,6 +13,8 @@ type Program struct {
1213
ToolSet ToolSet `json:"toolSet,omitempty"`
1314
}
1415

16+
type BuiltinFunc func(ctx context.Context, env []string, input string) (string, error)
17+
1518
type Tool struct {
1619
ID string `json:"id,omitempty"`
1720
Name string `json:"name,omitempty"`
@@ -20,6 +23,7 @@ type Tool struct {
2023
Instructions string `json:"instructions,omitempty"`
2124
Tools []string `json:"tools,omitempty"`
2225
ToolMapping map[string]string `json:"toolMapping,omitempty"`
26+
BuiltinFunc BuiltinFunc `json:"-"`
2327

2428
Vision bool `json:"vision,omitempty"`
2529
MaxTokens int `json:"maxTokens,omitempty"`

0 commit comments

Comments
 (0)