feat(agent): implement tool calling in agent mode
Add support for Grok to call tools (edit, scaffold, testgen, lint, commit) via JSON in ```tool blocks. Introduce HandleToolCall to parse and execute tool requests, integrating with existing commands. Update system prompt and chat loop to handle tool calls and feed results back.
This commit is contained in:
parent
875e34669c
commit
69c5d776e2
23
cmd/chat.go
23
cmd/chat.go
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gmgauthier.com/grokkit/config"
|
"gmgauthier.com/grokkit/config"
|
||||||
|
"gmgauthier.com/grokkit/internal/agent"
|
||||||
"gmgauthier.com/grokkit/internal/grok"
|
"gmgauthier.com/grokkit/internal/grok"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,10 +98,15 @@ func runChat(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if agentMode {
|
if agentMode {
|
||||||
systemPrompt["content"] = `You are Grok in Agent Mode.
|
systemPrompt["content"] = "You are Grok in Agent Mode.\n" +
|
||||||
You have access to tools: edit, scaffold, testgen, lint, commit, etc.
|
"You can call tools using this exact JSON format inside ```tool blocks:\n\n" +
|
||||||
Always use tools when the user wants you to change code, generate tests, lint or commit.
|
"```tool\n" +
|
||||||
Be concise and action-oriented. After every tool call, wait for the result.`
|
"{\"tool\": \"edit\", \"file\": \"main.go\", \"instruction\": \"Add error handling\"}\n" +
|
||||||
|
"```\n\n" +
|
||||||
|
"Available tools: edit, scaffold, testgen, lint, commit.\n\n" +
|
||||||
|
"Always use tools when the user asks you to change code, generate tests, lint, or commit.\n" +
|
||||||
|
"After every tool call, wait for the result before continuing.\n" +
|
||||||
|
"Be concise and action-oriented."
|
||||||
}
|
}
|
||||||
|
|
||||||
history := loadChatHistory()
|
history := loadChatHistory()
|
||||||
@ -138,6 +144,15 @@ Be concise and action-oriented. After every tool call, wait for the result.`
|
|||||||
color.Green("Grok > ")
|
color.Green("Grok > ")
|
||||||
reply := client.Stream(history, model)
|
reply := client.Stream(history, model)
|
||||||
|
|
||||||
|
// Agent mode: check for tool calls
|
||||||
|
if agentMode && strings.Contains(reply, "```tool") {
|
||||||
|
agent.HandleToolCall(reply, &history, model)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
history = append(history, map[string]string{"role": "assistant", "content": reply})
|
||||||
|
_ = saveChatHistory(history)
|
||||||
|
|
||||||
history = append(history, map[string]string{"role": "assistant", "content": reply})
|
history = append(history, map[string]string{"role": "assistant", "content": reply})
|
||||||
_ = saveChatHistory(history)
|
_ = saveChatHistory(history)
|
||||||
|
|
||||||
|
|||||||
95
internal/agent/tools.go
Normal file
95
internal/agent/tools.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
_ "fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"gmgauthier.com/grokkit/cmd"
|
||||||
|
"gmgauthier.com/grokkit/internal/git"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToolCall represents a tool request from Grok
|
||||||
|
type ToolCall struct {
|
||||||
|
Tool string `json:"tool"`
|
||||||
|
File string `json:"file,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
Instruction string `json:"instruction,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleToolCall parses and executes tool calls from Grok's response
|
||||||
|
func HandleToolCall(reply string, history *[]map[string]string, model string) {
|
||||||
|
// Look for ```tool ... ``` blocks
|
||||||
|
start := strings.Index(reply, "```tool")
|
||||||
|
if start == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
end := strings.Index(reply[start:], "```")
|
||||||
|
if end == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
block := strings.TrimSpace(reply[start+7 : start+end])
|
||||||
|
|
||||||
|
var tc ToolCall
|
||||||
|
if err := json.Unmarshal([]byte(block), &tc); err != nil {
|
||||||
|
color.Red("Failed to parse tool call: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Yellow("\n[Agent] Grok wants to call tool: %s", tc.Tool)
|
||||||
|
|
||||||
|
switch tc.Tool {
|
||||||
|
case "edit":
|
||||||
|
if tc.File == "" || tc.Instruction == "" {
|
||||||
|
color.Red("Invalid edit call — missing file or instruction")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
color.Cyan("Editing %s with instruction: %s", tc.File, tc.Instruction)
|
||||||
|
// Reuse the existing edit flow (it already does preview + confirm)
|
||||||
|
cmd.RunEditWithInstruction(tc.File, tc.Instruction)
|
||||||
|
|
||||||
|
case "scaffold":
|
||||||
|
if tc.Path == "" || tc.Description == "" {
|
||||||
|
color.Red("Invalid scaffold call")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
color.Cyan("Scaffolding %s: %s", tc.Path, tc.Description)
|
||||||
|
cmd.RunScaffoldWithDescription(tc.Path, tc.Description)
|
||||||
|
|
||||||
|
case "testgen":
|
||||||
|
if tc.File == "" {
|
||||||
|
color.Red("Invalid testgen call")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
color.Cyan("Generating tests for %s", tc.File)
|
||||||
|
cmd.RunTestgenWithFile(tc.File)
|
||||||
|
|
||||||
|
case "lint":
|
||||||
|
if tc.File == "" {
|
||||||
|
color.Red("Invalid lint call")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
color.Cyan("Linting %s", tc.File)
|
||||||
|
cmd.RunLintWithFile(tc.File)
|
||||||
|
|
||||||
|
case "commit":
|
||||||
|
if tc.Message == "" {
|
||||||
|
color.Red("Invalid commit call")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
color.Cyan("Committing with message: %s", tc.Message)
|
||||||
|
git.Run([]string{"commit", "-m", tc.Message})
|
||||||
|
|
||||||
|
default:
|
||||||
|
color.Red("Unknown tool: %s", tc.Tool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed result back to Grok
|
||||||
|
*history = append(*history, map[string]string{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": reply,
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user