feat: enhance go_eval tool with injected context bindings
- Modified GoEvalTool to accept a map of reflect.Value bindings. - Exported these bindings to the synthetic `jane/env` and `jane/env/env` packages in Yaegi. - Added comprehensive unit tests for the GoEvalTool to verify isolated execution and execution with bindings. - Aligns with Phase 1 of JANE_EVOLUTION_PLAN.md for autonomous task execution. Co-authored-by: hobbyistlabs-coder <267281733+hobbyistlabs-coder@users.noreply.github.com>
This commit is contained in:
parent
c31965f35e
commit
9fc542294c
2 changed files with 122 additions and 0 deletions
|
|
@ -3,6 +3,7 @@ package tools
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -37,6 +38,7 @@ func (b *threadSafeBuffer) Len() int {
|
||||||
type GoEvalTool struct {
|
type GoEvalTool struct {
|
||||||
workspace string
|
workspace string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
Bindings map[string]reflect.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGoEvalTool(workspace string) *GoEvalTool {
|
func NewGoEvalTool(workspace string) *GoEvalTool {
|
||||||
|
|
@ -46,6 +48,10 @@ func NewGoEvalTool(workspace string) *GoEvalTool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *GoEvalTool) SetBindings(bindings map[string]reflect.Value) {
|
||||||
|
t.Bindings = bindings
|
||||||
|
}
|
||||||
|
|
||||||
func (t *GoEvalTool) Name() string {
|
func (t *GoEvalTool) Name() string {
|
||||||
return "go_eval"
|
return "go_eval"
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +98,20 @@ func (t *GoEvalTool) Execute(ctx context.Context, args map[string]any) *ToolResu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.Bindings != nil && len(t.Bindings) > 0 {
|
||||||
|
exports := interp.Exports{
|
||||||
|
"jane/env/env": t.Bindings,
|
||||||
|
"jane/env": t.Bindings,
|
||||||
|
}
|
||||||
|
if err := i.Use(exports); err != nil {
|
||||||
|
return &ToolResult{
|
||||||
|
ForLLM: fmt.Sprintf("Failed to initialize injected bindings: %v", err),
|
||||||
|
ForUser: "Execution environment setup failed.",
|
||||||
|
IsError: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Channel to capture execution result
|
// Channel to capture execution result
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
||||||
102
pkg/tools/go_eval_test.go
Normal file
102
pkg/tools/go_eval_test.go
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGoEvalTool(t *testing.T) {
|
||||||
|
tool := NewGoEvalTool("/tmp/test_workspace")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("execute simple go code", func(t *testing.T) {
|
||||||
|
args := map[string]any{
|
||||||
|
"code": `
|
||||||
|
import "fmt"
|
||||||
|
func main() {
|
||||||
|
fmt.Println("hello from yaegi")
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tool.Execute(ctx, args)
|
||||||
|
if result.IsError {
|
||||||
|
t.Fatalf("Expected no error, got: %s", result.ForLLM)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(result.ForLLM, "hello from yaegi") {
|
||||||
|
t.Errorf("Expected output to contain 'hello from yaegi', got %q", result.ForLLM)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("execution timeout", func(t *testing.T) {
|
||||||
|
tool.timeout = 100 * time.Millisecond
|
||||||
|
defer func() { tool.timeout = 60 * time.Second }()
|
||||||
|
|
||||||
|
args := map[string]any{
|
||||||
|
"code": `
|
||||||
|
import "time"
|
||||||
|
func init() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tool.Execute(ctx, args)
|
||||||
|
if !result.IsError {
|
||||||
|
t.Fatal("Expected timeout error, got success")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result.ForLLM, "timed out") {
|
||||||
|
t.Errorf("Expected timeout message, got %q", result.ForLLM)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var TestWorkspace = "mock_workspace"
|
||||||
|
var DoMockTask = func() string {
|
||||||
|
return "mock task completed"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoEvalToolWithBindings(t *testing.T) {
|
||||||
|
tool := NewGoEvalTool("/tmp/test_workspace")
|
||||||
|
|
||||||
|
bindings := map[string]reflect.Value{
|
||||||
|
"Workspace": reflect.ValueOf(&TestWorkspace).Elem(),
|
||||||
|
"DoTask": reflect.ValueOf(&DoMockTask).Elem(),
|
||||||
|
}
|
||||||
|
|
||||||
|
tool.SetBindings(bindings)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("execute with bindings", func(t *testing.T) {
|
||||||
|
args := map[string]any{
|
||||||
|
"code": `
|
||||||
|
import "jane/env"
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
fmt.Println("workspace:", env.Workspace)
|
||||||
|
fmt.Println("task:", env.DoTask())
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tool.Execute(ctx, args)
|
||||||
|
if result.IsError {
|
||||||
|
t.Fatalf("Expected no error, got: %s", result.ForLLM)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(result.ForLLM, "workspace: mock_workspace") {
|
||||||
|
t.Errorf("Expected output to contain 'workspace: mock_workspace', got %q", result.ForLLM)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(result.ForLLM, "task: mock task completed") {
|
||||||
|
t.Errorf("Expected output to contain 'task: mock task completed', got %q", result.ForLLM)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue