Introduce "agents"
An agent is currently a name given to a system prompt and a set of tools which the agent has access to. This resolves the previous issue of the set of configured tools being available in *all* contexts, which wasn't always desired. Tools are now only available when an agent is explicitly requested using the `-a/--agent` flag. Agents are expected to be expanded on: the concept of task-specilized agents (e.g. coding), the ability to define a set of files an agent should always have access to for RAG purposes, etc. Other changes: - Removes the "tools" top-level config structure (though this is expected to come back along with the abillity to define custom tools). - Renamed `pkg/agent` to `pkg/agents`
This commit is contained in:
142
pkg/agents/toolbox/dir_tree.go
Normal file
142
pkg/agents/toolbox/dir_tree.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
toolutil "git.mlow.ca/mlow/lmcli/pkg/agents/toolbox/util"
|
||||
"git.mlow.ca/mlow/lmcli/pkg/api"
|
||||
)
|
||||
|
||||
const TREE_DESCRIPTION = `Retrieve a tree-like view of a directory's contents.
|
||||
|
||||
Use these results for your own reference in completing your task, they do not need to be shown to the user.
|
||||
|
||||
Example result:
|
||||
{
|
||||
"message": "success",
|
||||
"result": ".
|
||||
├── a_directory/
|
||||
│ ├── file1.txt (100 bytes)
|
||||
│ └── file2.txt (200 bytes)
|
||||
├── a_file.txt (123 bytes)
|
||||
└── another_file.txt (456 bytes)"
|
||||
}
|
||||
`
|
||||
|
||||
var DirTreeTool = api.ToolSpec{
|
||||
Name: "dir_tree",
|
||||
Description: TREE_DESCRIPTION,
|
||||
Parameters: []api.ToolParameter{
|
||||
{
|
||||
Name: "relative_path",
|
||||
Type: "string",
|
||||
Description: "If set, display the tree starting from this path relative to the current one.",
|
||||
},
|
||||
{
|
||||
Name: "depth",
|
||||
Type: "integer",
|
||||
Description: "Depth of directory recursion. Defaults to 0 (no recursion), maximum of 5.",
|
||||
},
|
||||
},
|
||||
Impl: func(tool *api.ToolSpec, args map[string]interface{}) (string, error) {
|
||||
var relativeDir string
|
||||
if tmp, ok := args["relative_path"]; ok {
|
||||
relativeDir, ok = tmp.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("expected string for relative_path, got %T", tmp)
|
||||
}
|
||||
}
|
||||
|
||||
var depth int = 0 // Default value if not provided
|
||||
if tmp, ok := args["depth"]; ok {
|
||||
switch v := tmp.(type) {
|
||||
case float64:
|
||||
depth = int(v)
|
||||
case string:
|
||||
var err error
|
||||
if depth, err = strconv.Atoi(v); err != nil {
|
||||
return "", fmt.Errorf("invalid `depth` value, expected integer but got string that cannot convert: %v", tmp)
|
||||
}
|
||||
depth = max(0, min(5, depth))
|
||||
default:
|
||||
return "", fmt.Errorf("expected int or string for max_depth, got %T", tmp)
|
||||
}
|
||||
}
|
||||
|
||||
result := tree(relativeDir, depth)
|
||||
ret, err := result.ToJson()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not serialize result: %v", err)
|
||||
}
|
||||
return ret, nil
|
||||
},
|
||||
}
|
||||
|
||||
func tree(path string, depth int) api.CallResult {
|
||||
if path == "" {
|
||||
path = "."
|
||||
}
|
||||
ok, reason := toolutil.IsPathWithinCWD(path)
|
||||
if !ok {
|
||||
return api.CallResult{Message: reason}
|
||||
}
|
||||
|
||||
var treeOutput strings.Builder
|
||||
treeOutput.WriteString(path + "\n")
|
||||
err := buildTree(&treeOutput, path, "", depth)
|
||||
if err != nil {
|
||||
return api.CallResult{
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return api.CallResult{Result: treeOutput.String()}
|
||||
}
|
||||
|
||||
func buildTree(output *strings.Builder, path string, prefix string, depth int) error {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, file := range files {
|
||||
if strings.HasPrefix(file.Name(), ".") {
|
||||
// Skip hidden files and directories
|
||||
continue
|
||||
}
|
||||
|
||||
isLast := i == len(files)-1
|
||||
var branch string
|
||||
if isLast {
|
||||
branch = "└── "
|
||||
} else {
|
||||
branch = "├── "
|
||||
}
|
||||
|
||||
info, _ := file.Info()
|
||||
size := info.Size()
|
||||
sizeStr := fmt.Sprintf(" (%d bytes)", size)
|
||||
|
||||
output.WriteString(prefix + branch + file.Name())
|
||||
if file.IsDir() {
|
||||
output.WriteString("/\n")
|
||||
if depth > 0 {
|
||||
var nextPrefix string
|
||||
if isLast {
|
||||
nextPrefix = prefix + " "
|
||||
} else {
|
||||
nextPrefix = prefix + "│ "
|
||||
}
|
||||
buildTree(output, filepath.Join(path, file.Name()), nextPrefix, depth-1)
|
||||
}
|
||||
} else {
|
||||
output.WriteString(sizeStr + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user