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:
2025-10-28 00:30:02 -04:00
parent 1c3ea7d1f1
commit 9a4ddf3fc1
8 changed files with 25 additions and 654 deletions

14
go.mod
View File

@@ -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

28
go.sum
View File

@@ -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=

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}