From 9a4ddf3fc1ea99694b5a749597c4c69d062ba51e Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Tue, 28 Oct 2025 00:30:02 -0400 Subject: [PATCH] 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. --- go.mod | 14 +- go.sum | 28 +- pkg/agents/agent.go | 3 + pkg/agents/console-new/agent.go | 397 ---------------------------- pkg/agents/console-new/container.go | 115 -------- pkg/agents/console-new/execution.go | 97 ------- pkg/agents/console-new/util.go | 23 -- pkg/agents/search.go | 2 +- 8 files changed, 25 insertions(+), 654 deletions(-) delete mode 100644 pkg/agents/console-new/agent.go delete mode 100644 pkg/agents/console-new/container.go delete mode 100644 pkg/agents/console-new/execution.go delete mode 100644 pkg/agents/console-new/util.go diff --git a/go.mod b/go.mod index f7dbca7..df2299a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/rocketlaunchr/google-search => github.com/chrisjoyce911/googl //replace gitea.stevedudenhoeffer.com/steve/go-llm => ../go-llm require ( - gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20250318064250-39453288ce2a + gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20251028042419-868acfae40b8 gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20250502021123-e9baf7910e00 github.com/Edw590/go-wolfram v0.0.0-20241010091529-fb9031908c5d github.com/advancedlogic/GoOse v0.0.0-20231203033844-ae6b36caf275 @@ -17,7 +17,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/lrstanley/go-ytdlp v0.0.0-20250501010938-80d02fe36936 github.com/opencontainers/image-spec v1.1.1 - github.com/playwright-community/playwright-go v0.5101.0 + github.com/playwright-community/playwright-go v0.5200.1 github.com/rocketlaunchr/google-search v1.1.6 github.com/urfave/cli v1.22.16 go.starlark.net v0.0.0-20250318223901-d9371fef63fe @@ -95,12 +95,12 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/net v0.46.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/api v0.231.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 045c694..7f0e34e 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4 cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= -gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20250318064250-39453288ce2a h1:LZriHuPVjdus7Haz+qEFYgr+g/eOdmeAvlbgk67DDHA= -gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20250318064250-39453288ce2a/go.mod h1:fzvvUfN8ej2u1ruCsABG+D+2dAPfOklInS4b1pvog1M= +gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20251028042419-868acfae40b8 h1:AaoLRGM7EFKtyx8+2vBuLoRmGxXdpkHpVF5XBtbWrXI= +gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20251028042419-868acfae40b8/go.mod h1:RjdXeJjOcnilhFf1O8cKOUaeGpuAazlQ2ectwgpjzbs= gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20250502021123-e9baf7910e00 h1:yPVQZG4xdENkWZi/9OLQcFSQb603ftWUTRct51Q64xc= gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20250502021123-e9baf7910e00/go.mod h1:RPbuI2VSwQJArwr4tdqmu+fEKlhpro5Cqtq6aC4Cp1w= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= @@ -206,8 +206,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= -github.com/playwright-community/playwright-go v0.5101.0 h1:gVCMZThDO76LJ/aCI27lpB8hEAWhZszeS0YB+oTxJp0= -github.com/playwright-community/playwright-go v0.5101.0/go.mod h1:kBNWs/w2aJ2ZUp1wEOOFLXgOqvppFngM5OS+qyhl+ZM= +github.com/playwright-community/playwright-go v0.5200.1 h1:Sm2oOuhqt0M5Y4kUi/Qh9w4cyyi3ZIWTBeGKImc2UVo= +github.com/playwright-community/playwright-go v0.5200.1/go.mod h1:UnnyQZaqUOO5ywAZu60+N4EiWReUqX1MQBBA3Oofvf8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -294,8 +294,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -335,8 +335,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= @@ -351,8 +351,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -372,8 +372,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -399,8 +399,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= diff --git a/pkg/agents/agent.go b/pkg/agents/agent.go index 92b81f2..64292d5 100644 --- a/pkg/agents/agent.go +++ b/pkg/agents/agent.go @@ -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) } diff --git a/pkg/agents/console-new/agent.go b/pkg/agents/console-new/agent.go deleted file mode 100644 index 9c3a391..0000000 --- a/pkg/agents/console-new/agent.go +++ /dev/null @@ -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 -} diff --git a/pkg/agents/console-new/container.go b/pkg/agents/console-new/container.go deleted file mode 100644 index 4e02f75..0000000 --- a/pkg/agents/console-new/container.go +++ /dev/null @@ -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 -} diff --git a/pkg/agents/console-new/execution.go b/pkg/agents/console-new/execution.go deleted file mode 100644 index 3395f87..0000000 --- a/pkg/agents/console-new/execution.go +++ /dev/null @@ -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 -} diff --git a/pkg/agents/console-new/util.go b/pkg/agents/console-new/util.go deleted file mode 100644 index 05fef49..0000000 --- a/pkg/agents/console-new/util.go +++ /dev/null @@ -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 -} diff --git a/pkg/agents/search.go b/pkg/agents/search.go index cb456af..1c071f3 100644 --- a/pkg/agents/search.go +++ b/pkg/agents/search.go @@ -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 }