Card: coding assistant
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ history/
|
|||||||
config.toml
|
config.toml
|
||||||
sysprompts/*
|
sysprompts/*
|
||||||
!sysprompts/alice_bob_carl.json
|
!sysprompts/alice_bob_carl.json
|
||||||
|
!sysprompts/coding_assistant.json
|
||||||
history_bak/
|
history_bak/
|
||||||
.aider*
|
.aider*
|
||||||
tags
|
tags
|
||||||
|
|||||||
19
bot.go
19
bot.go
@@ -886,6 +886,8 @@ func cleanChatBody() {
|
|||||||
|
|
||||||
// convertJSONToMapStringString unmarshals JSON into map[string]interface{} and converts all values to strings.
|
// convertJSONToMapStringString unmarshals JSON into map[string]interface{} and converts all values to strings.
|
||||||
func convertJSONToMapStringString(jsonStr string) (map[string]string, error) {
|
func convertJSONToMapStringString(jsonStr string) (map[string]string, error) {
|
||||||
|
// Extract JSON object from string - models may output extra text after JSON
|
||||||
|
jsonStr = extractJSON(jsonStr)
|
||||||
var raw map[string]interface{}
|
var raw map[string]interface{}
|
||||||
if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil {
|
if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -911,6 +913,23 @@ func convertJSONToMapStringString(jsonStr string) (map[string]string, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractJSON finds the first { and last } to extract only the JSON object
|
||||||
|
// This handles cases where models output extra text after JSON
|
||||||
|
func extractJSON(s string) string {
|
||||||
|
// Try direct parse first - if it works, return as-is
|
||||||
|
var dummy map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(s), &dummy); err == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
// Otherwise find JSON boundaries
|
||||||
|
start := strings.Index(s, "{")
|
||||||
|
end := strings.LastIndex(s, "}")
|
||||||
|
if start >= 0 && end > start {
|
||||||
|
return s[start : end+1]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// unmarshalFuncCall unmarshals a JSON tool call, converting numeric arguments to strings.
|
// unmarshalFuncCall unmarshals a JSON tool call, converting numeric arguments to strings.
|
||||||
func unmarshalFuncCall(jsonStr string) (*models.FuncCall, error) {
|
func unmarshalFuncCall(jsonStr string) (*models.FuncCall, error) {
|
||||||
type tempFuncCall struct {
|
type tempFuncCall struct {
|
||||||
|
|||||||
6
sysprompts/coding_assistant.json
Normal file
6
sysprompts/coding_assistant.json
Normal file
File diff suppressed because one or more lines are too long
39
tools.go
39
tools.go
@@ -648,12 +648,6 @@ func executeCommand(args map[string]string) []byte {
|
|||||||
return []byte(msg)
|
return []byte(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isCommandAllowed(command) {
|
|
||||||
msg := fmt.Sprintf("command '%s' is not allowed", command)
|
|
||||||
logger.Error(msg)
|
|
||||||
return []byte(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get arguments - handle both single arg and multiple args
|
// Get arguments - handle both single arg and multiple args
|
||||||
var cmdArgs []string
|
var cmdArgs []string
|
||||||
if args["args"] != "" {
|
if args["args"] != "" {
|
||||||
@@ -673,6 +667,12 @@ func executeCommand(args map[string]string) []byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isCommandAllowed(command, cmdArgs...) {
|
||||||
|
msg := fmt.Sprintf("command '%s' is not allowed", command)
|
||||||
|
logger.Error(msg)
|
||||||
|
return []byte(msg)
|
||||||
|
}
|
||||||
|
|
||||||
// Execute with timeout for safety
|
// Execute with timeout for safety
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -907,7 +907,19 @@ func todoDelete(args map[string]string) []byte {
|
|||||||
return jsonResult
|
return jsonResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCommandAllowed(command string) bool {
|
var gitReadSubcommands = map[string]bool{
|
||||||
|
"status": true,
|
||||||
|
"log": true,
|
||||||
|
"diff": true,
|
||||||
|
"show": true,
|
||||||
|
"branch": true,
|
||||||
|
"reflog": true,
|
||||||
|
"rev-parse": true,
|
||||||
|
"shortlog": true,
|
||||||
|
"describe": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCommandAllowed(command string, args ...string) bool {
|
||||||
allowedCommands := map[string]bool{
|
allowedCommands := map[string]bool{
|
||||||
"grep": true,
|
"grep": true,
|
||||||
"sed": true,
|
"sed": true,
|
||||||
@@ -937,8 +949,15 @@ func isCommandAllowed(command string) bool {
|
|||||||
"whoami": true,
|
"whoami": true,
|
||||||
"date": true,
|
"date": true,
|
||||||
"uname": true,
|
"uname": true,
|
||||||
|
"git": true,
|
||||||
}
|
}
|
||||||
return allowedCommands[command]
|
if !allowedCommands[command] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if command == "git" && len(args) > 0 {
|
||||||
|
return gitReadSubcommands[args[0]]
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func summarizeChat(args map[string]string) []byte {
|
func summarizeChat(args map[string]string) []byte {
|
||||||
@@ -1303,14 +1322,14 @@ var baseTools = []models.Tool{
|
|||||||
Type: "function",
|
Type: "function",
|
||||||
Function: models.ToolFunc{
|
Function: models.ToolFunc{
|
||||||
Name: "execute_command",
|
Name: "execute_command",
|
||||||
Description: "Execute a shell command safely. Use when you need to run system commands like grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname",
|
Description: "Execute a shell command safely. Use when you need to run system commands like grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname. Git is allowed for read-only operations: status, log, diff, show, branch, reflog, rev-parse, shortlog, describe.",
|
||||||
Parameters: models.ToolFuncParams{
|
Parameters: models.ToolFuncParams{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"command"},
|
Required: []string{"command"},
|
||||||
Properties: map[string]models.ToolArgProps{
|
Properties: map[string]models.ToolArgProps{
|
||||||
"command": models.ToolArgProps{
|
"command": models.ToolArgProps{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Description: "command to execute (only commands from whitelist are allowed: grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname",
|
Description: "command to execute (only commands from whitelist are allowed: grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname; git allowed for reads: status log diff show branch reflog rev-parse shortlog describe)",
|
||||||
},
|
},
|
||||||
"args": models.ToolArgProps{
|
"args": models.ToolArgProps{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
|
|||||||
Reference in New Issue
Block a user