Skip to content

Commit

Permalink
feat: manage node/python runtime for local files in dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ibuildthecloud committed Aug 10, 2024
1 parent a383d4f commit cb46358
Show file tree
Hide file tree
Showing 17 changed files with 552 additions and 14 deletions.
18 changes: 16 additions & 2 deletions pkg/repos/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Runtime interface {
ID() string
Supports(tool types.Tool, cmd []string) bool
Setup(ctx context.Context, tool types.Tool, dataRoot, toolSource string, env []string) ([]string, error)
GetHash(tool types.Tool) (string, error)
}

type noopRuntime struct {
Expand All @@ -37,6 +38,10 @@ func (n noopRuntime) ID() string {
return "none"
}

func (n noopRuntime) GetHash(_ types.Tool) (string, error) {
return "", nil
}

func (n noopRuntime) Supports(_ types.Tool, _ []string) bool {
return false
}
Expand Down Expand Up @@ -183,8 +188,13 @@ func (m *Manager) setup(ctx context.Context, runtime Runtime, tool types.Tool, e
locker.Lock(tool.ID)
defer locker.Unlock(tool.ID)

runtimeHash, err := runtime.GetHash(tool)
if err != nil {
return "", nil, err
}

target := filepath.Join(m.storageDir, tool.Source.Repo.Revision, tool.Source.Repo.Path, tool.Source.Repo.Name, runtime.ID())
targetFinal := filepath.Join(target, tool.Source.Repo.Path)
targetFinal := filepath.Join(target, tool.Source.Repo.Path+runtimeHash)
doneFile := targetFinal + ".done"
envData, err := os.ReadFile(doneFile)
if err == nil {
Expand Down Expand Up @@ -251,7 +261,11 @@ func (m *Manager) GetContext(ctx context.Context, tool types.Tool, cmd, env []st
for _, runtime := range m.runtimes {
if runtime.Supports(tool, cmd) {
log.Debugf("Runtime %s supports %v", runtime.ID(), cmd)
return m.setup(ctx, runtime, tool, env)
wd, env, err := m.setup(ctx, runtime, tool, env)
if isLocal {
wd = tool.WorkingDir
}
return wd, env, err
}
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/repos/runtimes/busybox/busybox.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func (r *Runtime) ID() string {
return "busybox"
}

func (r *Runtime) GetHash(_ types.Tool) (string, error) {
return "", nil
}

func (r *Runtime) Supports(_ types.Tool, cmd []string) bool {
if runtime.GOOS != "windows" {
return false
Expand Down
4 changes: 4 additions & 0 deletions pkg/repos/runtimes/golang/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func (r *Runtime) ID() string {
return "go" + r.Version
}

func (r *Runtime) GetHash(_ types.Tool) (string, error) {
return "", nil
}

func (r *Runtime) Supports(tool types.Tool, cmd []string) bool {
return tool.Source.IsGit() &&
len(cmd) > 0 && cmd[0] == "${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool"
Expand Down
26 changes: 22 additions & 4 deletions pkg/repos/runtimes/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ func (r *Runtime) ID() string {
return "node" + r.Version
}

func (r *Runtime) Supports(tool types.Tool, cmd []string) bool {
if _, hasPackageJSON := tool.MetaData[packageJSON]; !hasPackageJSON && !tool.Source.IsGit() {
return false
}
func (r *Runtime) Supports(_ types.Tool, cmd []string) bool {
for _, testCmd := range []string{"node", "npx", "npm"} {
if r.supports(testCmd, cmd) {
return true
Expand All @@ -61,6 +58,15 @@ func (r *Runtime) supports(testCmd string, cmd []string) bool {
return runtimeEnv.Matches(cmd, testCmd)
}

func (r *Runtime) GetHash(tool types.Tool) (string, error) {
if !tool.Source.IsGit() && tool.WorkingDir != "" {
if s, err := os.Stat(filepath.Join(tool.WorkingDir, packageJSON)); err == nil {
return hash.Digest(tool.WorkingDir + s.ModTime().String())[:7], nil
}
}
return "", nil
}

func (r *Runtime) Setup(ctx context.Context, tool types.Tool, dataRoot, toolSource string, env []string) ([]string, error) {
binPath, err := r.getRuntime(ctx, dataRoot)
if err != nil {
Expand All @@ -74,6 +80,8 @@ func (r *Runtime) Setup(ctx context.Context, tool types.Tool, dataRoot, toolSour

if _, ok := tool.MetaData[packageJSON]; ok {
newEnv = append(newEnv, "GPTSCRIPT_TMPDIR="+toolSource)
} else if !tool.Source.IsGit() && tool.WorkingDir != "" {
newEnv = append(newEnv, "GPTSCRIPT_TMPDIR="+tool.WorkingDir, "GPTSCRIPT_RUNTIME_DEV=true")
}

return newEnv, nil
Expand Down Expand Up @@ -120,6 +128,16 @@ func (r *Runtime) runNPM(ctx context.Context, tool types.Tool, toolSource, binDi
if err := os.WriteFile(filepath.Join(toolSource, packageJSON), []byte(contents+"\n"), 0644); err != nil {
return err
}
} else if !tool.Source.IsGit() {
if tool.WorkingDir == "" {
return nil
}
if _, err := os.Stat(filepath.Join(tool.WorkingDir, packageJSON)); errors.Is(fs.ErrNotExist, err) {
return nil
} else if err != nil {
return err
}
cmd.Dir = tool.WorkingDir
}
return cmd.Run()
}
Expand Down
38 changes: 30 additions & 8 deletions pkg/repos/runtimes/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import (
var releasesData []byte

const (
uvVersion = "uv==0.2.33"
requirementsTxt = "requirements.txt"
uvVersion = "uv==0.2.33"
requirementsTxt = "requirements.txt"
gptscriptRequirementsTxt = "requirements-gptscript.txt"
)

type Release struct {
Expand All @@ -47,10 +48,7 @@ func (r *Runtime) ID() string {
return "python" + r.Version
}

func (r *Runtime) Supports(tool types.Tool, cmd []string) bool {
if _, hasRequirements := tool.MetaData[requirementsTxt]; !hasRequirements && !tool.Source.IsGit() {
return false
}
func (r *Runtime) Supports(_ types.Tool, cmd []string) bool {
if runtimeEnv.Matches(cmd, r.ID()) {
return true
}
Expand Down Expand Up @@ -177,6 +175,22 @@ func (r *Runtime) getReleaseAndDigest() (string, string, error) {
return "", "", fmt.Errorf("failed to find an python runtime for %s", r.Version)
}

func (r *Runtime) GetHash(tool types.Tool) (string, error) {
if !tool.Source.IsGit() && tool.WorkingDir != "" {
if _, ok := tool.MetaData[requirementsTxt]; ok {
return "", nil
}
for _, req := range []string{gptscriptRequirementsTxt, requirementsTxt} {
reqFile := filepath.Join(tool.WorkingDir, req)
if s, err := os.Stat(reqFile); err == nil && !s.IsDir() {
return hash.Digest(tool.WorkingDir + s.ModTime().String())[:7], nil
}
}
}

return "", nil
}

func (r *Runtime) runPip(ctx context.Context, tool types.Tool, toolSource, binDir string, env []string) error {
log.InfofCtx(ctx, "Running pip in %s", toolSource)
if content, ok := tool.MetaData[requirementsTxt]; ok {
Expand All @@ -189,8 +203,16 @@ func (r *Runtime) runPip(ctx context.Context, tool types.Tool, toolSource, binDi
return cmd.Run()
}

for _, req := range []string{"requirements-gptscript.txt", requirementsTxt} {
reqFile := filepath.Join(toolSource, req)
reqPath := toolSource
if !tool.Source.IsGit() {
if tool.WorkingDir == "" {
return nil
}
reqPath = tool.WorkingDir
}

for _, req := range []string{gptscriptRequirementsTxt, requirementsTxt} {
reqFile := filepath.Join(reqPath, req)
if s, err := os.Stat(reqFile); err == nil && !s.IsDir() {
cmd := debugcmd.New(ctx, uvBin(binDir), "pip", "install", "-r", reqFile)
cmd.Env = env
Expand Down
23 changes: 23 additions & 0 deletions pkg/tests/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1018,3 +1018,26 @@ func TestRuntimes(t *testing.T) {
})
r.RunDefault()
}

func TestRuntimesLocalDev(t *testing.T) {
r := tester.NewRunner(t)
r.RespondWith(tester.Result{
Func: types.CompletionFunctionCall{
Name: "py",
Arguments: "{}",
},
}, tester.Result{
Func: types.CompletionFunctionCall{
Name: "node",
Arguments: "{}",
},
}, tester.Result{
Func: types.CompletionFunctionCall{
Name: "bash",
Arguments: "{}",
},
})
r.RunDefault()
_ = os.RemoveAll("testdata/TestRuntimesLocalDev/node_modules")
_ = os.RemoveAll("testdata/TestRuntimesLocalDev/package-lock.json")
}
16 changes: 16 additions & 0 deletions pkg/tests/testdata/TestRuntimesLocalDev/call1-resp.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
`{
"role": "assistant",
"content": [
{
"toolCall": {
"index": 0,
"id": "call_1",
"function": {
"name": "py",
"arguments": "{}"
}
}
}
],
"usage": {}
}`
37 changes: 37 additions & 0 deletions pkg/tests/testdata/TestRuntimesLocalDev/call1.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
`{
"model": "gpt-4o",
"tools": [
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:py",
"name": "py",
"parameters": null
}
},
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:node",
"name": "node",
"parameters": null
}
},
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:bash",
"name": "bash",
"parameters": null
}
}
],
"messages": [
{
"role": "system",
"content": [
{
"text": "Dummy"
}
],
"usage": {}
}
]
}`
16 changes: 16 additions & 0 deletions pkg/tests/testdata/TestRuntimesLocalDev/call2-resp.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
`{
"role": "assistant",
"content": [
{
"toolCall": {
"index": 1,
"id": "call_2",
"function": {
"name": "node",
"arguments": "{}"
}
}
}
],
"usage": {}
}`
70 changes: 70 additions & 0 deletions pkg/tests/testdata/TestRuntimesLocalDev/call2.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
`{
"model": "gpt-4o",
"tools": [
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:py",
"name": "py",
"parameters": null
}
},
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:node",
"name": "node",
"parameters": null
}
},
{
"function": {
"toolID": "testdata/TestRuntimesLocalDev/test.gpt:bash",
"name": "bash",
"parameters": null
}
}
],
"messages": [
{
"role": "system",
"content": [
{
"text": "Dummy"
}
],
"usage": {}
},
{
"role": "assistant",
"content": [
{
"toolCall": {
"index": 0,
"id": "call_1",
"function": {
"name": "py",
"arguments": "{}"
}
}
}
],
"usage": {}
},
{
"role": "tool",
"content": [
{
"text": "py worked\r\n"
}
],
"toolCall": {
"index": 0,
"id": "call_1",
"function": {
"name": "py",
"arguments": "{}"
}
},
"usage": {}
}
]
}`
16 changes: 16 additions & 0 deletions pkg/tests/testdata/TestRuntimesLocalDev/call3-resp.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
`{
"role": "assistant",
"content": [
{
"toolCall": {
"index": 2,
"id": "call_3",
"function": {
"name": "bash",
"arguments": "{}"
}
}
}
],
"usage": {}
}`
Loading

0 comments on commit cb46358

Please sign in to comment.