Remove deprecated console_new package
				
					
				
			Deleted the `console_new` package, including its agent, container, execution, and utility files. Updated dependencies and replaced references to align with the maintained code structure.
This commit is contained in:
		| @@ -7,6 +7,7 @@ import ( | ||||
| 	"sync/atomic" | ||||
|  | ||||
| 	gollm "gitea.stevedudenhoeffer.com/steve/go-llm" | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| ) | ||||
|  | ||||
| // Agent is essentially the bones of a chat agent.  It has a model and a toolbox, and can be used to call the model | ||||
| @@ -184,6 +185,8 @@ func (a Agent) _callAndExecuteParallel(ctx context.Context, parallel bool, msgs | ||||
|  | ||||
| 	req, err := a.ToRequest(msgs...) | ||||
|  | ||||
| 	spew.Dump(req) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return CallAndExecuteResults{}, fmt.Errorf("failed to create request: %w", err) | ||||
| 	} | ||||
|   | ||||
| @@ -1,397 +0,0 @@ | ||||
| package console_new | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/docker/docker/api/types/container" | ||||
| 	"github.com/docker/docker/api/types/mount" | ||||
| 	"github.com/docker/docker/client" | ||||
|  | ||||
| 	gollm "gitea.stevedudenhoeffer.com/steve/go-llm" | ||||
|  | ||||
| 	"gitea.stevedudenhoeffer.com/steve/answer/pkg/agents" | ||||
| 	"gitea.stevedudenhoeffer.com/steve/answer/pkg/agents/shared" | ||||
| ) | ||||
|  | ||||
| type Agent struct { | ||||
| 	agents.Agent | ||||
| 	// Model is the chat completion model to use | ||||
| 	Model gollm.ChatCompletion | ||||
|  | ||||
| 	OnLoopComplete func(ctx context.Context, knowledge shared.Knowledge) error | ||||
|  | ||||
| 	OnCommandStart func(ctx context.Context, command string) error | ||||
| 	OnCommandDone  func(ctx context.Context, command string, output string, err error) error | ||||
|  | ||||
| 	OnDone func(ctx context.Context, knowledge shared.Knowledge) error | ||||
|  | ||||
| 	ContextualInformation []string | ||||
|  | ||||
| 	MaxCommands int | ||||
| } | ||||
|  | ||||
| type Response struct { | ||||
| 	Knowledge shared.Knowledge | ||||
| 	DataDir   string | ||||
| 	OutputDir string | ||||
| } | ||||
|  | ||||
| // Answer will give the model access to an ubuntu console with python and pip installed, and then ask the model to | ||||
| // do what is necessary to answer the question. | ||||
| func (a Agent) Answer(ctx context.Context, questions []string) (Response, error) { | ||||
| 	var res Response | ||||
|  | ||||
| 	a.Agent = agents.NewAgent(a.Model, gollm.NewToolBox()).WithMaxCalls(200) | ||||
|  | ||||
| 	if a.MaxCommands <= 0 { | ||||
| 		a.MaxCommands = 20 // Default to 20 commands as per requirements | ||||
| 	} | ||||
|  | ||||
| 	res.Knowledge = shared.Knowledge{ | ||||
| 		OriginalQuestions:  questions, | ||||
| 		RemainingQuestions: questions, | ||||
| 	} | ||||
|  | ||||
| 	// create temporary directories for data and output | ||||
| 	dataDir, err := os.MkdirTemp("", "console-data-") | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
|  | ||||
| 	outputDir, err := os.MkdirTemp("", "console-output-") | ||||
| 	if err != nil { | ||||
| 		os.RemoveAll(dataDir) | ||||
| 		return res, err | ||||
| 	} | ||||
|  | ||||
| 	res.DataDir = dataDir | ||||
| 	res.OutputDir = outputDir | ||||
|  | ||||
| 	cl, err := client.NewClientWithOpts(client.FromEnv) | ||||
| 	if err != nil { | ||||
| 		os.RemoveAll(dataDir) | ||||
| 		os.RemoveAll(outputDir) | ||||
| 		return res, err | ||||
| 	} | ||||
| 	defer cl.Close() | ||||
|  | ||||
| 	mounts := []mount.Mount{ | ||||
| 		{ | ||||
| 			Type:   mount.TypeBind, | ||||
| 			Source: dataDir, | ||||
| 			Target: "/data", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Type:   mount.TypeBind, | ||||
| 			Source: outputDir, | ||||
| 			Target: "/output", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	c, err := CreateContainer(ctx, cl, ContainerConfig{ | ||||
| 		Config: &container.Config{ | ||||
| 			Image:      "ubuntu:latest", | ||||
| 			Cmd:        []string{"tail", "-f", "/dev/null"}, | ||||
| 			Tty:        true, | ||||
| 			WorkingDir: "/data", | ||||
| 		}, | ||||
| 		HostConfig: &container.HostConfig{ | ||||
| 			AutoRemove: true, | ||||
| 			Mounts:     mounts, | ||||
| 		}, | ||||
| 		Name: filepath.Base(dataDir), | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		os.RemoveAll(dataDir) | ||||
| 		os.RemoveAll(outputDir) | ||||
| 		return res, fmt.Errorf("failed to create container: %w", err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		_ = c.Close(ctx) | ||||
| 	}() | ||||
|  | ||||
| 	slog.Info("starting container", "dataDir", dataDir, "outputDir", outputDir, "container", fmt.Sprintf("%+v", c)) | ||||
| 	err = c.Start(ctx) | ||||
| 	if err != nil { | ||||
| 		os.RemoveAll(dataDir) | ||||
| 		os.RemoveAll(outputDir) | ||||
| 		return res, fmt.Errorf("failed to start container: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Run the model | ||||
|  | ||||
| 	var history executions | ||||
| 	var keepGoing = true | ||||
|  | ||||
| 	// Initial setup - install basic tools | ||||
| 	setupCmd, setupErr := c.Sudo(ctx, "apt-get update && apt-get install -y curl wget git python3 python3-pip") | ||||
| 	if setupErr == nil { | ||||
| 		history = append(history, execution{ | ||||
| 			Command:      "sudo apt-get update && apt-get install -y curl wget git python3 python3-pip", | ||||
| 			Output:       setupCmd, | ||||
| 			WhatILearned: []string{"Basic tools installed: curl, wget, git, python3, pip"}, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	tools := map[string]gollm.Function{ | ||||
| 		"exit": gollm.NewFunction( | ||||
| 			"exit", | ||||
| 			"exit the container", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				RemainingQuestions []string `description:"any remaining questions that remain unanswered"` | ||||
| 			}) (any, error) { | ||||
| 				keepGoing = false | ||||
| 				return "exiting", nil | ||||
| 			}), | ||||
|  | ||||
| 		"write_data": gollm.NewFunction( | ||||
| 			"write_data", | ||||
| 			"write a file in the /data directory", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				Filename string `description:"The name of the file to write"` | ||||
| 				Content  string `description:"The content of the file to write"` | ||||
| 			}) (any, error) { | ||||
| 				target, err := SafeJoinPath(dataDir, args.Filename) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				// Ensure directory exists | ||||
| 				dir := filepath.Dir(target) | ||||
| 				if err := os.MkdirAll(dir, 0755); err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				f, err := os.Create(target) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
| 				defer f.Close() | ||||
|  | ||||
| 				_, err = f.WriteString(args.Content) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				return "wrote file to /data/" + args.Filename, nil | ||||
| 			}), | ||||
|  | ||||
| 		"write_output": gollm.NewFunction( | ||||
| 			"write_output", | ||||
| 			"write a file in the /output directory (files here will be available after the agent completes)", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				Filename string `description:"The name of the file to write"` | ||||
| 				Content  string `description:"The content of the file to write"` | ||||
| 			}) (any, error) { | ||||
| 				target, err := SafeJoinPath(outputDir, args.Filename) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				// Ensure directory exists | ||||
| 				dir := filepath.Dir(target) | ||||
| 				if err := os.MkdirAll(dir, 0755); err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				f, err := os.Create(target) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
| 				defer f.Close() | ||||
|  | ||||
| 				_, err = f.WriteString(args.Content) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				return "wrote file to /output/" + args.Filename, nil | ||||
| 			}), | ||||
|  | ||||
| 		"read_data": gollm.NewFunction( | ||||
| 			"read_data", | ||||
| 			"read a file from the /data directory", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				Filename string `description:"The name of the file to read"` | ||||
| 			}) (any, error) { | ||||
| 				target, err := SafeJoinPath(dataDir, args.Filename) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				b, err := os.ReadFile(target) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				return string(b), nil | ||||
| 			}), | ||||
|  | ||||
| 		"read_output": gollm.NewFunction( | ||||
| 			"read_output", | ||||
| 			"read a file from the /output directory", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				Filename string `description:"The name of the file to read"` | ||||
| 			}) (any, error) { | ||||
| 				target, err := SafeJoinPath(outputDir, args.Filename) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				b, err := os.ReadFile(target) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
|  | ||||
| 				return string(b), nil | ||||
| 			}), | ||||
|  | ||||
| 		"execute": gollm.NewFunction( | ||||
| 			"execute", | ||||
| 			"execute a command in the container", | ||||
| 			func(ctx *gollm.Context, args struct { | ||||
| 				Command string   `description:"The command to execute"` | ||||
| 				Learn   []string `description:"What you learned from this command (optional)"` | ||||
| 				ToLearn []string `description:"What you still need to learn (optional)"` | ||||
| 			}) (any, error) { | ||||
| 				if len(history) >= a.MaxCommands { | ||||
| 					return "Command limit reached. You've used all " + fmt.Sprintf("%d", a.MaxCommands) + " allowed commands.", nil | ||||
| 				} | ||||
|  | ||||
| 				if a.OnCommandStart != nil { | ||||
| 					err := a.OnCommandStart(ctx, args.Command) | ||||
| 					if err != nil { | ||||
| 						return "", err | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				var res string | ||||
| 				// if the command starts with sudo then we need to use the sudo function | ||||
| 				if strings.HasPrefix(args.Command, "sudo ") { | ||||
| 					res, err = c.Sudo(ctx, args.Command[5:]) | ||||
| 				} else { | ||||
| 					res, err = c.Execute(ctx, args.Command) | ||||
| 				} | ||||
|  | ||||
| 				if a.OnCommandDone != nil { | ||||
| 					err = a.OnCommandDone(ctx, args.Command, res, nil) | ||||
| 					if err != nil { | ||||
| 						return "", err | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				history = append(history, execution{ | ||||
| 					Command:               args.Command, | ||||
| 					Output:                res, | ||||
| 					WhatILearned:          args.Learn, | ||||
| 					WhatIStillNeedToLearn: args.ToLearn, | ||||
| 				}) | ||||
|  | ||||
| 				return res, nil | ||||
| 			}), | ||||
|  | ||||
| 		"summarize_knowledge": gollm.NewFunction( | ||||
| 			"summarize_knowledge", | ||||
| 			"summarize what you've learned so far", | ||||
| 			func(ctx *gollm.Context, args struct{}) (any, error) { | ||||
| 				var learned []string | ||||
| 				var toLearn []string | ||||
|  | ||||
| 				for _, exec := range history { | ||||
| 					learned = append(learned, exec.WhatILearned...) | ||||
| 					toLearn = append(toLearn, exec.WhatIStillNeedToLearn...) | ||||
| 				} | ||||
|  | ||||
| 				summary := "Knowledge Summary:\n" | ||||
| 				if len(learned) > 0 { | ||||
| 					summary += "What I've learned:\n- " + strings.Join(learned, "\n- ") + "\n\n" | ||||
| 				} else { | ||||
| 					summary += "I haven't learned anything specific yet.\n\n" | ||||
| 				} | ||||
|  | ||||
| 				if len(toLearn) > 0 { | ||||
| 					summary += "What I still need to learn:\n- " + strings.Join(toLearn, "\n- ") | ||||
| 				} else { | ||||
| 					summary += "I don't have any specific learning goals at the moment." | ||||
| 				} | ||||
|  | ||||
| 				return summary, nil | ||||
| 			}), | ||||
| 	} | ||||
|  | ||||
| 	for i := 0; i < a.MaxCommands && len(history) < a.MaxCommands && keepGoing; i++ { | ||||
|  | ||||
| 		systemPrompt := `You are now in a shell in a container of the ubuntu:latest image to answer a question asked by the user.  | ||||
| You have full control over a bash shell inside this Docker container. | ||||
|  | ||||
| Important directories: | ||||
| - /data: A temporary directory with the lifespan of your processing. Use this for working files. | ||||
| - /output: Files placed here will be returned to the caller after you're done. Use this for final results. | ||||
|  | ||||
| You can execute up to ` + fmt.Sprintf("%d", a.MaxCommands) + ` commands total. You're currently on command ` + fmt.Sprintf("%d", len(history)+1) + ` of ` + fmt.Sprintf("%d", a.MaxCommands) + `. | ||||
|  | ||||
| For each command, you should: | ||||
| 1. Think about what you need to learn | ||||
| 2. Execute the command using the "execute" function | ||||
| 3. Analyze the output and record what you learned | ||||
| 4. Plan your next command based on this knowledge | ||||
|  | ||||
| You can write files directly to /data or /output using the write_data and write_output functions. | ||||
| When you are done, use "exit" to exit the container.` | ||||
|  | ||||
| 		var toolbox []gollm.Function | ||||
|  | ||||
| 		// Add all tools | ||||
| 		toolbox = append(toolbox, | ||||
| 			tools["exit"], | ||||
| 			tools["write_data"], | ||||
| 			tools["write_output"], | ||||
| 			tools["read_data"], | ||||
| 			tools["read_output"], | ||||
| 			tools["summarize_knowledge"], | ||||
| 		) | ||||
|  | ||||
| 		// Only add execute if we haven't reached the command limit | ||||
| 		if len(history) < a.MaxCommands { | ||||
| 			toolbox = append(toolbox, tools["execute"]) | ||||
| 		} | ||||
|  | ||||
| 		kw := shared.KnowledgeWorker{ | ||||
| 			Model:                 a.Model, | ||||
| 			ToolBox:               gollm.NewToolBox(toolbox...), | ||||
| 			ContextualInformation: a.ContextualInformation, | ||||
| 			OnNewFunction: func(ctx context.Context, funcName string, args string) (any, error) { | ||||
| 				slog.Info("new function called", "function name", funcName, "args", args) | ||||
| 				return nil, nil | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		slog.Info("answering question", "question", questions[0]) | ||||
| 		r, err := kw.Answer(ctx, &res.Knowledge, systemPrompt, "", "", history.ToGeneralButLastMessageHistory(), func(res gollm.ToolCallResponse) { | ||||
| 		}) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return res, fmt.Errorf("error answering question: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if len(r.Knowledge) > 0 { | ||||
| 			slog.Info("answered question and learned", "knowledge", r.Knowledge) | ||||
| 		} else { | ||||
| 			slog.Info("answered question and learned nothing") | ||||
| 		} | ||||
|  | ||||
| 		res.Knowledge, err = a.KnowledgeIntegrate(ctx, res.Knowledge, r) | ||||
| 		if err != nil { | ||||
| 			return res, fmt.Errorf("error integrating knowledge: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		slog.Info("knowledge integrated", "question", questions[0], "knowledge", res.Knowledge) | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| @@ -1,115 +0,0 @@ | ||||
| package console_new | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
|  | ||||
| 	"github.com/docker/docker/api/types/container" | ||||
| 	"github.com/docker/docker/api/types/network" | ||||
| 	"github.com/docker/docker/client" | ||||
| 	v1 "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| ) | ||||
|  | ||||
| type Container struct { | ||||
| 	client *client.Client | ||||
| 	id     string | ||||
| } | ||||
|  | ||||
| func (c Container) Start(ctx context.Context) error { | ||||
| 	return c.client.ContainerStart(ctx, c.id, container.StartOptions{}) | ||||
| } | ||||
|  | ||||
| func (c Container) Close(ctx context.Context) error { | ||||
| 	timeout := 10 | ||||
| 	if err := c.client.ContainerStop(ctx, c.id, container.StopOptions{ | ||||
| 		Timeout: &timeout, | ||||
| 	}); err != nil { | ||||
| 		// If stop fails, force kill | ||||
| 		if err := c.client.ContainerKill(ctx, c.id, "SIGKILL"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Remove container and volumes | ||||
| 	removeOptions := container.RemoveOptions{ | ||||
| 		RemoveVolumes: true, | ||||
| 		Force:         true, | ||||
| 	} | ||||
|  | ||||
| 	return c.client.ContainerRemove(ctx, c.id, removeOptions) | ||||
| } | ||||
|  | ||||
| func (c Container) execUserCmd(ctx context.Context, user string, cmd string) (string, error) { | ||||
| 	slog.Info("executing command", "user", user, "cmd", cmd) | ||||
| 	var cmdStr = []string{"/bin/bash", "-c"} | ||||
|  | ||||
| 	cmdStr = append(cmdStr, cmd) | ||||
|  | ||||
| 	slog.Info("executing command", "user", user, "cmd", fmt.Sprintf("%#v", cmdStr)) | ||||
| 	exec, err := c.client.ContainerExecCreate(ctx, c.id, container.ExecOptions{ | ||||
| 		Cmd:          cmdStr, | ||||
| 		User:         user, | ||||
| 		AttachStdout: true, | ||||
| 		AttachStderr: true, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	resp, err := c.client.ContainerExecAttach(ctx, exec.ID, container.ExecAttachOptions{}) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer resp.Close() | ||||
|  | ||||
| 	output, err := io.ReadAll(resp.Reader) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	// Wait for command to finish and get exit code | ||||
| 	inspectResp, err := c.client.ContainerExecInspect(ctx, exec.ID) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	slog.Info("command finished", "output", string(output), "err", err, "resp", inspectResp) | ||||
|  | ||||
| 	if inspectResp.ExitCode != 0 { | ||||
| 		return string(output), fmt.Errorf("command exited with code %d", inspectResp.ExitCode) | ||||
| 	} | ||||
|  | ||||
| 	return string(output), nil | ||||
| } | ||||
| func (c Container) Execute(ctx context.Context, cmd string) (string, error) { | ||||
| 	return c.execUserCmd(ctx, "", cmd) | ||||
| } | ||||
|  | ||||
| func (c Container) ExecuteAs(ctx context.Context, user string, cmd string) (string, error) { | ||||
| 	return c.execUserCmd(ctx, user, cmd) | ||||
| } | ||||
|  | ||||
| func (c Container) Sudo(ctx context.Context, cmd string) (string, error) { | ||||
| 	return c.execUserCmd(ctx, "root", cmd) | ||||
| } | ||||
|  | ||||
| type ContainerConfig struct { | ||||
| 	Config     *container.Config | ||||
| 	HostConfig *container.HostConfig | ||||
| 	NetConfig  *network.NetworkingConfig | ||||
| 	Platform   *v1.Platform | ||||
| 	Name       string | ||||
| } | ||||
|  | ||||
| func CreateContainer(ctx context.Context, cl *client.Client, cfg ContainerConfig) (*Container, error) { | ||||
| 	resp, err := cl.ContainerCreate(ctx, cfg.Config, cfg.HostConfig, cfg.NetConfig, cfg.Platform, cfg.Name) | ||||
|  | ||||
| 	slog.Info("creating container", "resp", resp, "err", err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &Container{client: cl, id: resp.ID}, nil | ||||
| } | ||||
| @@ -1,97 +0,0 @@ | ||||
| package console_new | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	gollm "gitea.stevedudenhoeffer.com/steve/go-llm" | ||||
| ) | ||||
|  | ||||
| type execution struct { | ||||
| 	Command               string | ||||
| 	Output                string | ||||
| 	WhatILearned          []string | ||||
| 	WhatIStillNeedToLearn []string | ||||
| } | ||||
|  | ||||
| const kMaxLenCommandSummary = 200 | ||||
| const kMaxLenCommandOutputSummary = 200 | ||||
|  | ||||
| func (e execution) ToGeneralMessageHistory() gollm.Message { | ||||
| 	if len(e.Command) > kMaxLenCommandSummary { | ||||
| 		e.Command = e.Command[:kMaxLenCommandSummary] + "... (truncated)" | ||||
| 	} | ||||
|  | ||||
| 	if len(e.Output) > kMaxLenCommandOutputSummary { | ||||
| 		e.Output = e.Output[:kMaxLenCommandOutputSummary] + "... (truncated)" | ||||
| 	} | ||||
|  | ||||
| 	text := "# " + e.Command + "\n" + e.Output | ||||
|  | ||||
| 	return gollm.Message{ | ||||
| 		Role: gollm.RoleUser, | ||||
| 		Text: text, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e execution) ToDetailedMessageHistory() gollm.Message { | ||||
| 	prompt := "$ " | ||||
| 	if strings.HasPrefix(e.Command, "sudo ") { | ||||
| 		prompt = "# " | ||||
| 		e.Command = e.Command[5:] | ||||
| 	} | ||||
|  | ||||
| 	text := prompt + strings.TrimSpace(e.Command) + "\n" + e.Output | ||||
|  | ||||
| 	if len(e.WhatILearned) > 0 { | ||||
| 		text += "\n\nWhat I learned:\n" + strings.Join(e.WhatILearned, "\n") | ||||
| 	} else { | ||||
| 		text += "\n\nI didn't learn anything new." | ||||
| 	} | ||||
|  | ||||
| 	if len(e.WhatIStillNeedToLearn) > 0 { | ||||
| 		text += "\n\nWhat I still need to learn:\n" + strings.Join(e.WhatIStillNeedToLearn, "\n") | ||||
| 	} else { | ||||
| 		text += "\n\nI don't need to learn anything else." | ||||
| 	} | ||||
|  | ||||
| 	return gollm.Message{ | ||||
| 		Role: gollm.RoleUser, | ||||
| 		Text: text, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type executions []execution | ||||
|  | ||||
| func (e executions) ToGeneralMessageHistory() []gollm.Message { | ||||
| 	var messages []gollm.Message | ||||
|  | ||||
| 	for _, v := range e { | ||||
| 		messages = append(messages, v.ToGeneralMessageHistory()) | ||||
| 	} | ||||
|  | ||||
| 	return messages | ||||
| } | ||||
|  | ||||
| func (e executions) ToGeneralButLastMessageHistory() []gollm.Message { | ||||
| 	var messages []gollm.Message | ||||
|  | ||||
| 	for i, v := range e { | ||||
| 		if i == len(e)-1 { | ||||
| 			messages = append(messages, v.ToDetailedMessageHistory()) | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		messages = append(messages, v.ToGeneralMessageHistory()) | ||||
| 	} | ||||
|  | ||||
| 	return messages | ||||
| } | ||||
| func (e executions) ToDetailedMessageHistory() []gollm.Message { | ||||
| 	var messages []gollm.Message | ||||
|  | ||||
| 	for _, v := range e { | ||||
| 		messages = append(messages, v.ToDetailedMessageHistory()) | ||||
| 	} | ||||
|  | ||||
| 	return messages | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| package console_new | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func SafeJoinPath(tempDir, fileName string) (string, error) { | ||||
| 	// Clean both paths | ||||
| 	tempDir = filepath.Clean(tempDir) | ||||
| 	fileName = filepath.Clean(fileName) | ||||
|  | ||||
| 	// Join paths and clean result | ||||
| 	fullPath := filepath.Clean(filepath.Join(tempDir, fileName)) | ||||
|  | ||||
| 	// Verify the path is still within tempDir | ||||
| 	if !strings.HasPrefix(fullPath, tempDir+string(filepath.Separator)) { | ||||
| 		return "", fmt.Errorf("invalid path") | ||||
| 	} | ||||
|  | ||||
| 	return fullPath, nil | ||||
| } | ||||
| @@ -51,7 +51,7 @@ func (a Agent) SearchAndUseTools(ctx context.Context, searchQuery string, questi | ||||
|  | ||||
| 	browser, ok := ctx.Value("browser").(extractor.Browser) | ||||
| 	if !ok { | ||||
| 		b, err := extractor.NewPlayWrightBrowser(extractor.PlayWrightBrowserOptions{}) | ||||
| 		b, err := extractor.NewPlayWrightBrowser(ctx, extractor.PlayWrightBrowserOptions{}) | ||||
| 		if err != nil { | ||||
| 			return knowledge, err | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user