Skip to content

feat: add sys.ls #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion pkg/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ import (
)

var tools = map[string]types.Tool{
"sys.ls": {
Parameters: types.Parameters{
Description: "Lists the contents of a directory",
Arguments: types.ObjectSchema(
"dir", "The directory to list"),
},
BuiltinFunc: SysLs,
},
"sys.read": {
Parameters: types.Parameters{
Description: "Reads the contents of a file",
Expand Down Expand Up @@ -268,6 +276,37 @@ func SysExec(ctx context.Context, env []string, input string) (string, error) {
return string(out), err
}

func SysLs(_ context.Context, _ []string, input string) (string, error) {
var params struct {
Dir string `json:"dir,omitempty"`
}
if err := json.Unmarshal([]byte(input), &params); err != nil {
return "", err
}

if params.Dir == "" {
params.Dir = "."
}

entries, err := os.ReadDir(params.Dir)
if errors.Is(err, fs.ErrNotExist) {
return fmt.Sprintf("directory does not exist: %s", params.Dir), nil
} else if err != nil {
return "", err
}

var result []string
for _, entry := range entries {
if entry.IsDir() {
result = append(result, entry.Name()+"/")
} else {
result = append(result, entry.Name())
}
}

return strings.Join(result, "\n"), nil
}

func SysRead(ctx context.Context, env []string, input string) (string, error) {
var params struct {
Filename string `json:"filename,omitempty"`
Expand All @@ -282,10 +321,15 @@ func SysRead(ctx context.Context, env []string, input string) (string, error) {

log.Debugf("Reading file %s", params.Filename)
data, err := os.ReadFile(params.Filename)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return fmt.Sprintf("The file %s does not exist", params.Filename), nil
} else if err != nil {
return "", err
}

if len(data) == 0 {
return fmt.Sprintf("The file %s has no contents", params.Filename), nil
}
return string(data), nil
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/cli/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ func (e *Eval) Run(cmd *cobra.Command, args []string) error {
return err
}

opts := e.gptscript.NewGPTScriptOpts()
opts, err := e.gptscript.NewGPTScriptOpts()
if err != nil {
return err
}

runner, err := gptscript.New(&opts)
if err != nil {
return err
Expand Down
43 changes: 39 additions & 4 deletions pkg/cli/gptscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"sort"
"strconv"
"strings"

"github.com/acorn-io/cmd"
Expand Down Expand Up @@ -50,6 +51,8 @@ type GPTScript struct {
Server bool `usage:"Start server" local:"true"`
ListenAddress string `usage:"Server listen address" default:"127.0.0.1:9090" local:"true"`
Chdir string `usage:"Change current working directory" short:"C"`
Daemon bool `usage:"Run tool as a daemon" local:"true" hidden:"true"`
Ports string `usage:"The port range to use for ephemeral daemon ports (ex: 11000-12000)" hidden:"true"`
}

func New() *cobra.Command {
Expand All @@ -67,14 +70,33 @@ func (r *GPTScript) NewRunContext(cmd *cobra.Command) context.Context {
return ctx
}

func (r *GPTScript) NewGPTScriptOpts() gptscript.Options {
return gptscript.Options{
func (r *GPTScript) NewGPTScriptOpts() (gptscript.Options, error) {
opts := gptscript.Options{
Cache: cache.Options(r.CacheOptions),
OpenAI: openai.Options(r.OpenAIOptions),
Monitor: monitor.Options(r.DisplayOptions),
Quiet: r.Quiet,
Env: os.Environ(),
}

if r.Ports != "" {
start, end, _ := strings.Cut(r.Ports, "-")
startNum, err := strconv.ParseInt(strings.TrimSpace(start), 10, 64)
if err != nil {
return gptscript.Options{}, fmt.Errorf("invalid port range: %s", r.Ports)
}
var endNum int64
if end != "" {
endNum, err = strconv.ParseInt(strings.TrimSpace(end), 10, 64)
if err != nil {
return gptscript.Options{}, fmt.Errorf("invalid port range: %s", r.Ports)
}
}
opts.Runner.StartPort = startNum
opts.Runner.EndPort = endNum
}

return opts, nil
}

func (r *GPTScript) Customize(cmd *cobra.Command) {
Expand Down Expand Up @@ -205,8 +227,12 @@ func (r *GPTScript) PrintOutput(toolInput, toolOutput string) (err error) {
return
}

func (r *GPTScript) Run(cmd *cobra.Command, args []string) error {
gptOpt := r.NewGPTScriptOpts()
func (r *GPTScript) Run(cmd *cobra.Command, args []string) (retErr error) {
gptOpt, err := r.NewGPTScriptOpts()
if err != nil {
return err
}

ctx := cmd.Context()

if r.Server {
Expand Down Expand Up @@ -236,6 +262,15 @@ func (r *GPTScript) Run(cmd *cobra.Command, args []string) error {
return err
}

if r.Daemon {
prg = prg.SetBlocking()
defer func() {
if retErr == nil {
<-ctx.Done()
}
}()
}

if r.ListTools {
return r.listTools(ctx, gptScript, prg)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/engine/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ type Ports struct {
daemonWG sync.WaitGroup
}

func (p *Ports) SetPorts(start, end int64) {
p.startPort = start
p.endPort = end
}

func (p *Ports) CloseDaemons() {
p.daemonLock.Lock()
if p.daemonCtx == nil {
Expand Down
23 changes: 21 additions & 2 deletions pkg/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,26 @@ type Monitor interface {
type Options struct {
MonitorFactory MonitorFactory `usage:"-"`
RuntimeManager engine.RuntimeManager `usage:"-"`
StartPort int64 `usage:"-"`
EndPort int64 `usage:"-"`
}

func complete(opts ...Options) (result Options) {
for _, opt := range opts {
result.MonitorFactory = types.FirstSet(opt.MonitorFactory, result.MonitorFactory)
result.RuntimeManager = types.FirstSet(opt.RuntimeManager, result.RuntimeManager)
result.StartPort = types.FirstSet(opt.StartPort, result.StartPort)
result.EndPort = types.FirstSet(opt.EndPort, result.EndPort)
}
if result.MonitorFactory == nil {
result.MonitorFactory = noopFactory{}
}
if result.EndPort == 0 {
result.EndPort = result.StartPort
}
if result.StartPort == 0 {
result.StartPort = result.EndPort
}
return
}

Expand All @@ -49,11 +59,20 @@ type Runner struct {
func New(client engine.Model, opts ...Options) (*Runner, error) {
opt := complete(opts...)

return &Runner{
runner := &Runner{
c: client,
factory: opt.MonitorFactory,
runtimeManager: opt.RuntimeManager,
}, nil
}

if opt.StartPort != 0 {
if opt.EndPort < opt.StartPort {
return nil, fmt.Errorf("invalid port range: %d-%d", opt.StartPort, opt.EndPort)
}
runner.ports.SetPorts(opt.StartPort, opt.EndPort)
}

return runner, nil
}

func (r *Runner) Close() {
Expand Down