Compare commits
8 Commits
3859084fd8
...
6f9b79afa1
Author | SHA1 | Date | |
---|---|---|---|
6f9b79afa1 | |||
42f7b7aa29 | |||
9976c59f58 | |||
4b85b005dd | |||
59487d5721 | |||
1fc0af56df | |||
fa27f83630 | |||
b229c42811 |
@ -22,7 +22,7 @@ func (m *Message) FriendlyRole() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conversation) GenerateTitle() error {
|
func (c *Conversation) GenerateTitle() error {
|
||||||
const header = "Generate a consise 4-5 word title for the conversation below."
|
const header = "Generate a short title for the conversation below."
|
||||||
prompt := fmt.Sprintf("%s\n\n---\n\n%s", header, c.FormatForExternalPrompting())
|
prompt := fmt.Sprintf("%s\n\n---\n\n%s", header, c.FormatForExternalPrompting())
|
||||||
|
|
||||||
messages := []Message{
|
messages := []Message{
|
||||||
|
@ -35,8 +35,11 @@ type AvailableTool struct {
|
|||||||
Impl func(arguments map[string]interface{}) (string, error)
|
Impl func(arguments map[string]interface{}) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
var AvailableTools = map[string]AvailableTool{
|
||||||
READ_DIR_DESCRIPTION = `Return the contents of the CWD (current working directory).
|
"read_dir": {
|
||||||
|
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
||||||
|
Name: "read_dir",
|
||||||
|
Description: `Return the contents of the CWD (current working directory).
|
||||||
|
|
||||||
Results are returned as JSON in the following format:
|
Results are returned as JSON in the following format:
|
||||||
{
|
{
|
||||||
@ -50,46 +53,7 @@ Results are returned as JSON in the following format:
|
|||||||
}
|
}
|
||||||
|
|
||||||
For files, size represents the size (in bytes) of the file.
|
For files, size represents the size (in bytes) of the file.
|
||||||
For directories, size represents the number of entries in that directory.`
|
For directories, size represents the number of entries in that directory.`,
|
||||||
|
|
||||||
READ_FILE_DESCRIPTION = `Read the contents of a text file relative to the current working directory.
|
|
||||||
|
|
||||||
Each line of the file is prefixed with its line number and a tabs (\t) to make
|
|
||||||
it make it easier to see which lines to change for other modifications.
|
|
||||||
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"message": "success", // if successful, or a different message indicating failure
|
|
||||||
"result": "1\tthe contents\n2\tof the file\n"
|
|
||||||
}`
|
|
||||||
|
|
||||||
WRITE_FILE_DESCRIPTION = `Write the provided contents to a file relative to the current working directory.
|
|
||||||
|
|
||||||
Note: only use this tool when you've been explicitly asked to create or write to a file.
|
|
||||||
|
|
||||||
When using this function, you do not need to share the content you intend to write with the user first.
|
|
||||||
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"message": "success", // if successful, or a different message indicating failure
|
|
||||||
}`
|
|
||||||
|
|
||||||
FILE_INSERT_LINES_DESCRIPTION = `Insert lines into a file, must specify path.
|
|
||||||
|
|
||||||
Make sure your inserts match the flow and indentation of surrounding content.`
|
|
||||||
|
|
||||||
FILE_REPLACE_LINES_DESCRIPTION = `Replace or remove a range of lines within a file, must specify path.
|
|
||||||
|
|
||||||
Useful for re-writing snippets/blocks of code or entire functions.
|
|
||||||
|
|
||||||
Be cautious with your edits. When replacing, ensure the replacement content matches the flow and indentation of surrounding content.`
|
|
||||||
)
|
|
||||||
|
|
||||||
var AvailableTools = map[string]AvailableTool{
|
|
||||||
"read_dir": {
|
|
||||||
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
|
||||||
Name: "read_dir",
|
|
||||||
Description: READ_DIR_DESCRIPTION,
|
|
||||||
Parameters: FunctionParameters{
|
Parameters: FunctionParameters{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]FunctionParameter{
|
Properties: map[string]FunctionParameter{
|
||||||
@ -114,8 +78,17 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
},
|
},
|
||||||
"read_file": {
|
"read_file": {
|
||||||
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
||||||
Name: "read_file",
|
Name: "read_file",
|
||||||
Description: READ_FILE_DESCRIPTION,
|
Description: `Read the contents of a text file relative to the current working directory.
|
||||||
|
|
||||||
|
Each line of the file is prefixed with its line number and a tabs (\t) to make
|
||||||
|
it make it easier to see which lines to change for other modifications.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
"message": "success", // if successful, or a different message indicating failure
|
||||||
|
"result": "1\tthe contents\n2\tof the file\n"
|
||||||
|
}`,
|
||||||
Parameters: FunctionParameters{
|
Parameters: FunctionParameters{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]FunctionParameter{
|
Properties: map[string]FunctionParameter{
|
||||||
@ -141,8 +114,13 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
},
|
},
|
||||||
"write_file": {
|
"write_file": {
|
||||||
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
||||||
Name: "write_file",
|
Name: "write_file",
|
||||||
Description: WRITE_FILE_DESCRIPTION,
|
Description: `Write the provided contents to a file relative to the current working directory.
|
||||||
|
|
||||||
|
Result is returned as JSON in the following format:
|
||||||
|
{
|
||||||
|
"message": "success", // if successful, or a different message indicating failure
|
||||||
|
}`,
|
||||||
Parameters: FunctionParameters{
|
Parameters: FunctionParameters{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]FunctionParameter{
|
Properties: map[string]FunctionParameter{
|
||||||
@ -178,10 +156,28 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
return WriteFile(path, content), nil
|
return WriteFile(path, content), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"file_insert_lines": {
|
"modify_file": {
|
||||||
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
||||||
Name: "file_insert_lines",
|
Name: "modify_file",
|
||||||
Description: FILE_INSERT_LINES_DESCRIPTION,
|
Description: `Perform complex line-based modifications to a file.
|
||||||
|
|
||||||
|
Line ranges are inclusive. If 'start_line' is specified but 'end_line' is not,
|
||||||
|
'end_line' gets set to the last line of the file.
|
||||||
|
|
||||||
|
To replace or remove a single line, *set start_line and end_line to the same value*
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* Insert the lines "hello<new line>world" at line 10, preserving other content:
|
||||||
|
{"path": "myfile", "operation": "insert", "start_line": 10, "content": "hello\nworld"}
|
||||||
|
|
||||||
|
* Remove lines 45 up to and including 54:
|
||||||
|
{"path": "myfile", "operation": "remove", "start_line": 45, "end_line": 54}
|
||||||
|
|
||||||
|
* Replace content from line 10 to 25:
|
||||||
|
{"path": "myfile", "operation": "replace", "start_line": 10, "end_line": 25, "content": "i\nwas\nhere"}
|
||||||
|
|
||||||
|
* Replace contents of entire the file:
|
||||||
|
{"path": "myfile", "operation": "replace", "start_line": 0, "content": "i\nwas\nhere"}`,
|
||||||
Parameters: FunctionParameters{
|
Parameters: FunctionParameters{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]FunctionParameter{
|
Properties: map[string]FunctionParameter{
|
||||||
@ -189,72 +185,24 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
Type: "string",
|
Type: "string",
|
||||||
Description: "Path of the file to be modified, relative to the current working directory.",
|
Description: "Path of the file to be modified, relative to the current working directory.",
|
||||||
},
|
},
|
||||||
"position": {
|
"operation": {
|
||||||
Type: "integer",
|
|
||||||
Description: `Which line to insert content *before*.`,
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Description: `The content to insert.`,
|
Description: `The the type of modification to make to the file. One of: insert, remove, replace`,
|
||||||
},
|
|
||||||
},
|
|
||||||
Required: []string{"path", "position", "content"},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
Impl: func(args map[string]interface{}) (string, error) {
|
|
||||||
tmp, ok := args["path"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("path parameter to write_file was not included.")
|
|
||||||
}
|
|
||||||
path, ok := tmp.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("Invalid path in function arguments: %v", tmp)
|
|
||||||
}
|
|
||||||
var position int
|
|
||||||
tmp, ok = args["position"]
|
|
||||||
if ok {
|
|
||||||
tmp, ok := tmp.(float64)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("Invalid position in function arguments: %v", tmp)
|
|
||||||
}
|
|
||||||
position = int(tmp)
|
|
||||||
}
|
|
||||||
var content string
|
|
||||||
tmp, ok = args["content"]
|
|
||||||
if ok {
|
|
||||||
content, ok = tmp.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("Invalid content in function arguments: %v", tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FileInsertLines(path, position, content), nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"file_replace_lines": {
|
|
||||||
Tool: openai.Tool{Type: "function", Function: openai.FunctionDefinition{
|
|
||||||
Name: "file_replace_lines",
|
|
||||||
Description: FILE_REPLACE_LINES_DESCRIPTION,
|
|
||||||
Parameters: FunctionParameters{
|
|
||||||
Type: "object",
|
|
||||||
Properties: map[string]FunctionParameter{
|
|
||||||
"path": {
|
|
||||||
Type: "string",
|
|
||||||
Description: "Path of the file to be modified, relative to the current working directory.",
|
|
||||||
},
|
},
|
||||||
"start_line": {
|
"start_line": {
|
||||||
Type: "integer",
|
Type: "integer",
|
||||||
Description: `Line number which specifies the start of the replacement range (inclusive).`,
|
Description: `(Optional) Where to start making a modification (insert, remove, and replace).`,
|
||||||
},
|
},
|
||||||
"end_line": {
|
"end_line": {
|
||||||
Type: "integer",
|
Type: "integer",
|
||||||
Description: `Line number which specifies the end of the replacement range (inclusive). If unset, range extends to end of file.`,
|
Description: `(Optional) Where to stop making a modification (remove or replace, end of file if omitted).`,
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Description: `Content to replace specified range. Omit to remove the specified range.`,
|
Description: `(Optional) The content to insert, or replace with.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Required: []string{"path", "start_line"},
|
Required: []string{"path", "operation"},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Impl: func(args map[string]interface{}) (string, error) {
|
Impl: func(args map[string]interface{}) (string, error) {
|
||||||
@ -266,6 +214,14 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("Invalid path in function arguments: %v", tmp)
|
return "", fmt.Errorf("Invalid path in function arguments: %v", tmp)
|
||||||
}
|
}
|
||||||
|
tmp, ok = args["operation"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("operation parameter to modify_file was not included.")
|
||||||
|
}
|
||||||
|
operation, ok := tmp.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("Invalid operation in function arguments: %v", tmp)
|
||||||
|
}
|
||||||
var start_line int
|
var start_line int
|
||||||
tmp, ok = args["start_line"]
|
tmp, ok = args["start_line"]
|
||||||
if ok {
|
if ok {
|
||||||
@ -293,7 +249,7 @@ var AvailableTools = map[string]AvailableTool{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileReplaceLines(path, start_line, end_line, content), nil
|
return ModifyFile(path, operation, content, start_line, end_line), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -494,7 +450,7 @@ func WriteFile(path string, content string) string {
|
|||||||
return resultToJson(FunctionResult{})
|
return resultToJson(FunctionResult{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileInsertLines(path string, position int, content string) string {
|
func ModifyFile(path string, operation string, content string, startLine int, endLine int) string {
|
||||||
ok, res := isPathWithinCWD(path)
|
ok, res := isPathWithinCWD(path)
|
||||||
if !ok {
|
if !ok {
|
||||||
return resultToJson(*res)
|
return resultToJson(*res)
|
||||||
@ -513,16 +469,42 @@ func FileInsertLines(path string, position int, content string) string {
|
|||||||
data = []byte{}
|
data = []byte{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if position < 1 {
|
if startLine < 0 {
|
||||||
return resultToJson(FunctionResult{Message: "start_line cannot be less than 1"})
|
return resultToJson(FunctionResult{Message: "start_line cannot be less than 0"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split the content by newline to process lines
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.Split(string(data), "\n")
|
||||||
contentLines := strings.Split(strings.Trim(content, "\n"), "\n")
|
contentLines := strings.Split(strings.Trim(content, "\n"), "\n")
|
||||||
|
|
||||||
before := lines[:position-1]
|
switch operation {
|
||||||
after := lines[position-1:]
|
case "insert":
|
||||||
lines = append(before, append(contentLines, after...)...)
|
// Insert new lines
|
||||||
|
before := lines[:startLine-1]
|
||||||
|
after := append(contentLines, lines[startLine-1:]...)
|
||||||
|
lines = append(before, after...)
|
||||||
|
case "remove":
|
||||||
|
// Remove lines
|
||||||
|
if endLine == 0 || endLine > len(lines) {
|
||||||
|
endLine = len(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines[:startLine-1], lines[endLine:]...)
|
||||||
|
case "replace":
|
||||||
|
// Replace the lines between start_line and end_line
|
||||||
|
if endLine == 0 || endLine > len(lines) {
|
||||||
|
endLine = len(lines)
|
||||||
|
}
|
||||||
|
if startLine == 0 {
|
||||||
|
// model likely trying to replace contents, must start at line 1
|
||||||
|
startLine = 1
|
||||||
|
}
|
||||||
|
before := lines[:startLine-1]
|
||||||
|
after := lines[endLine:]
|
||||||
|
lines = append(before, append(contentLines, after...)...)
|
||||||
|
default:
|
||||||
|
return resultToJson(FunctionResult{Message: fmt.Sprintf("Invalid operation: %s", operation)})
|
||||||
|
}
|
||||||
|
|
||||||
newContent := strings.Join(lines, "\n")
|
newContent := strings.Join(lines, "\n")
|
||||||
|
|
||||||
@ -534,49 +516,3 @@ func FileInsertLines(path string, position int, content string) string {
|
|||||||
|
|
||||||
return resultToJson(FunctionResult{Result: newContent})
|
return resultToJson(FunctionResult{Result: newContent})
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileReplaceLines(path string, startLine int, endLine int, content string) string {
|
|
||||||
ok, res := isPathWithinCWD(path)
|
|
||||||
if !ok {
|
|
||||||
return resultToJson(*res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the existing file's content
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return resultToJson(FunctionResult{Message: fmt.Sprintf("Could not read path: %s", err.Error())})
|
|
||||||
}
|
|
||||||
_, err = os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return resultToJson(FunctionResult{Message: fmt.Sprintf("Could not create new file: %s", err.Error())})
|
|
||||||
}
|
|
||||||
data = []byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if startLine < 1 {
|
|
||||||
return resultToJson(FunctionResult{Message: "start_line cannot be less than 1"})
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(string(data), "\n")
|
|
||||||
contentLines := strings.Split(strings.Trim(content, "\n"), "\n")
|
|
||||||
|
|
||||||
if endLine == 0 || endLine > len(lines) {
|
|
||||||
endLine = len(lines)
|
|
||||||
}
|
|
||||||
|
|
||||||
before := lines[:startLine-1]
|
|
||||||
after := lines[endLine:]
|
|
||||||
|
|
||||||
lines = append(before, append(contentLines, after...)...)
|
|
||||||
newContent := strings.Join(lines, "\n")
|
|
||||||
|
|
||||||
// Join the lines and write back to the file
|
|
||||||
err = os.WriteFile(path, []byte(newContent), 0644)
|
|
||||||
if err != nil {
|
|
||||||
return resultToJson(FunctionResult{Message: fmt.Sprintf("Could not write to path: %s", err.Error())})
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultToJson(FunctionResult{Result: newContent})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user