Skip to content

Add tools distribution for python #121

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 1 commit into from
Mar 7, 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
20 changes: 19 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/hexops/autogold/v2 v2.1.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/olahol/melody v1.1.4
github.com/rs/cors v1.10.1
github.com/samber/lo v1.38.1
github.com/sashabaranov/go-openai v1.20.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
Expand All @@ -22,33 +24,49 @@ require (

require (
github.com/acorn-io/baaah v0.0.0-20240119160309-2a58ee757bbd // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/bombsimon/logrusr/v4 v4.0.0 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.16.1 // indirect
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/hexops/valast v1.4.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/nightlyone/lockfile v1.0.0 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo/v2 v2.13.0 // indirect
github.com/onsi/gomega v1.29.0 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/samber/slog-logrus v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
Expand Down
240 changes: 240 additions & 0 deletions go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Options struct {
CacheDir string `usage:"Directory to store cache (default: $XDG_CACHE_HOME/gptscript)"`
}

func complete(opts ...Options) (result Options) {
func Complete(opts ...Options) (result Options) {
for _, opt := range opts {
result.CacheDir = types.FirstSet(opt.CacheDir, result.CacheDir)
result.Cache = types.FirstSet(opt.Cache, result.Cache)
Expand All @@ -48,7 +48,7 @@ func WithNoCache(ctx context.Context) context.Context {
}

func New(opts ...Options) (*Client, error) {
opt := complete(opts...)
opt := Complete(opts...)
if err := os.MkdirAll(opt.CacheDir, 0755); err != nil {
return nil, err
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/gptscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/gptscript-ai/gptscript/pkg/monitor"
"github.com/gptscript-ai/gptscript/pkg/mvl"
"github.com/gptscript-ai/gptscript/pkg/openai"
"github.com/gptscript-ai/gptscript/pkg/repos/runtimes"
"github.com/gptscript-ai/gptscript/pkg/runner"
"github.com/gptscript-ai/gptscript/pkg/server"
"github.com/gptscript-ai/gptscript/pkg/types"
Expand Down Expand Up @@ -225,6 +226,7 @@ func (r *GPTScript) Run(cmd *cobra.Command, args []string) error {
MonitorFactory: monitor.NewConsole(monitor.Options(r.DisplayOptions), monitor.Options{
DisplayProgress: !*r.Quiet,
}),
RuntimeManager: runtimes.Default(cache.Complete(cache.Options(r.CacheOptions)).CacheDir),
})
if err != nil {
return err
Expand Down
20 changes: 20 additions & 0 deletions pkg/debugcmd/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package debugcmd

import (
"context"
"os"
"os/exec"
)

func New(ctx context.Context, arg string, args ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, arg, args...)
SetupDebug(cmd)
return cmd
}

func SetupDebug(cmd *exec.Cmd) {
if log.IsDebug() {
cmd.Stdout = os.Stdout
}
cmd.Stderr = os.Stderr
}
5 changes: 5 additions & 0 deletions pkg/debugcmd/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package debugcmd

import "github.com/gptscript-ai/gptscript/pkg/mvl"

var log = mvl.Package()
129 changes: 85 additions & 44 deletions pkg/engine/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"sync/atomic"

Expand Down Expand Up @@ -40,12 +42,7 @@ func (e *Engine) runCommand(ctx context.Context, tool types.Tool, input string)
return tool.BuiltinFunc(ctx, e.Env, input)
}

var extraEnv []string
if tool.WorkingDir != "" {
extraEnv = append(extraEnv, "GPTSCRIPT_TOOL_DIR="+tool.WorkingDir)
}

cmd, stop, err := e.newCommand(ctx, extraEnv, tool.Instructions, input)
cmd, stop, err := e.newCommand(ctx, nil, tool, input)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -74,62 +71,106 @@ func (e *Engine) runCommand(ctx context.Context, tool types.Tool, input string)
return output.String(), nil
}

func (e *Engine) newCommand(ctx context.Context, extraEnv []string, instructions, input string) (*exec.Cmd, func(), error) {
env := append(e.Env[:], extraEnv...)
data := map[string]any{}

dec := json.NewDecoder(bytes.NewReader([]byte(input)))
dec.UseNumber()
func (e *Engine) getRuntimeEnv(ctx context.Context, tool types.Tool, cmd, env []string) ([]string, error) {
var (
workdir = tool.WorkingDir
err error
)
if e.RuntimeManager != nil {
workdir, env, err = e.RuntimeManager.GetContext(ctx, tool, cmd, env)
if err != nil {
return nil, err
}
workdir = filepath.Join(workdir, tool.Source.Repo.Path)
}
return append(env, "GPTSCRIPT_TOOL_DIR="+workdir), nil
}

func envAsMapAndDeDup(env []string) (sortedEnv []string, _ map[string]string) {
envMap := map[string]string{}
var keys []string
for _, env := range env {
key, value, _ := strings.Cut(env, "=")
if _, existing := envMap[key]; !existing {
keys = append(keys, key)
}
envMap[key] = value
}
sort.Strings(keys)
for _, key := range keys {
sortedEnv = append(sortedEnv, key+"="+envMap[key])
}

return sortedEnv, envMap
}

var ignoreENV = map[string]struct{}{
"PATH": {},
"GPTSCRIPT_TOOL_DIR": {},
}

func appendEnv(env []string, k, v string) []string {
for _, k := range []string{k, strings.ToUpper(strings.ReplaceAll(k, "-", "_"))} {
if _, ignore := ignoreENV[k]; !ignore {
env = append(env, k+"="+v)
}
}
return env
}

func appendInputAsEnv(env []string, input string) []string {
data := map[string]any{}
dec := json.NewDecoder(bytes.NewReader([]byte(input)))
dec.UseNumber()

if err := json.Unmarshal([]byte(input), &data); err != nil {
// ignore invalid JSON
return env
}

if err := json.Unmarshal([]byte(input), &data); err == nil {
for k, v := range data {
envName := strings.ToUpper(strings.ReplaceAll(k, "-", "_"))
switch val := v.(type) {
case string:
envMap[envName] = val
env = append(env, envName+"="+val)
envMap[k] = val
env = append(env, k+"="+val)
case json.Number:
envMap[envName] = string(val)
env = append(env, envName+"="+string(val))
envMap[k] = string(val)
env = append(env, k+"="+string(val))
case bool:
envMap[envName] = fmt.Sprint(val)
env = append(env, envName+"="+fmt.Sprint(val))
envMap[k] = fmt.Sprint(val)
env = append(env, k+"="+fmt.Sprint(val))
default:
data, err := json.Marshal(val)
if err == nil {
envMap[envName] = string(data)
env = append(env, envName+"="+string(data))
envMap[k] = string(data)
env = append(env, k+"="+string(data))
}
for k, v := range data {
switch val := v.(type) {
case string:
env = appendEnv(env, k, val)
case json.Number:
env = appendEnv(env, k, string(val))
case bool:
env = appendEnv(env, k, fmt.Sprint(val))
default:
data, err := json.Marshal(val)
if err == nil {
env = appendEnv(env, k, string(data))
}
}
}

interpreter, rest, _ := strings.Cut(instructions, "\n")
interpreter = strings.TrimSpace(interpreter)[2:]
return env
}

interpreter = os.Expand(interpreter, func(s string) string {
return envMap[s]
})
func (e *Engine) newCommand(ctx context.Context, extraEnv []string, tool types.Tool, input string) (*exec.Cmd, func(), error) {
env := append(e.Env[:], extraEnv...)
env = appendInputAsEnv(env, input)

interpreter, rest, _ := strings.Cut(tool.Instructions, "\n")
interpreter = strings.TrimSpace(interpreter)[2:]

args, err := shlex.Split(interpreter)
if err != nil {
return nil, nil, err
}

env, err = e.getRuntimeEnv(ctx, tool, args, env)
if err != nil {
return nil, nil, err
}

env, envMap := envAsMapAndDeDup(env)
for i, arg := range args {
args[i] = os.Expand(arg, func(s string) string {
return envMap[s]
})
}

var (
cmdArgs = args[1:]
stop = func() {}
Expand Down
3 changes: 2 additions & 1 deletion pkg/engine/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (e *Engine) startDaemon(_ context.Context, tool types.Tool) (string, error)

instructions := strings.TrimPrefix(tool.Instructions, types.DaemonPrefix)
instructions, path := getPath(instructions)
tool.Instructions = types.CommandPrefix + instructions

port, ok := daemonPorts[tool.ID]
url := fmt.Sprintf("http://127.0.0.1:%d%s", port, path)
Expand All @@ -92,7 +93,7 @@ func (e *Engine) startDaemon(_ context.Context, tool types.Tool) (string, error)
cmd, stop, err := e.newCommand(ctx, []string{
fmt.Sprintf("PORT=%d", port),
},
types.CommandPrefix+instructions,
tool,
"{}",
)
if err != nil {
Expand Down
11 changes: 8 additions & 3 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@ type Model interface {
Call(ctx context.Context, messageRequest types.CompletionRequest, status chan<- types.CompletionStatus) (*types.CompletionMessage, error)
}

type RuntimeManager interface {
GetContext(ctx context.Context, tool types.Tool, cmd, env []string) (string, []string, error)
}

type Engine struct {
Model Model
Env []string
Progress chan<- types.CompletionStatus
Model Model
RuntimeManager RuntimeManager
Env []string
Progress chan<- types.CompletionStatus
}

type State struct {
Expand Down
16 changes: 14 additions & 2 deletions pkg/hash/sha256.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@ import (
"encoding/json"
)

func ID(parts ...string) string {
d := sha256.New()
for i, part := range parts {
if i > 0 {
d.Write([]byte{0x00})
}
d.Write([]byte(part))
}
hash := d.Sum(nil)
return hex.EncodeToString(hash[:])
}

func Digest(obj any) string {
data, err := json.Marshal(obj)
if err != nil {
panic(err)
}

hash := sha256.Sum224(data)
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:])
}

Expand All @@ -32,6 +44,6 @@ func Encode(obj any) string {
panic(err)
}

hash := sha256.Sum224(data)
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:])
}
Loading