Commit c98e237
Changed files (2)
internal
internal/del/assistant.go
@@ -10,6 +10,8 @@ import (
"os/exec"
"regexp"
"strings"
+
+ "github.com/ollama/ollama/api"
)
// Tool represents a tool Del can run
@@ -21,9 +23,10 @@ type Tool struct {
// Del represents the assistant instance
type Del struct {
- aiProvider AIProvider
- tools map[string]*Tool
- mcpServers map[string]string
+ aiProvider AIProvider
+ tools map[string]*Tool
+ mcpServers map[string]string
+ chatHistory []api.Message // for stateful interaction
}
// NewDel creates a new assistant
@@ -99,12 +102,19 @@ func NewDel(provider AIProvider) *Del {
aiProvider: provider,
tools: tools,
mcpServers: make(map[string]string),
+ chatHistory: []api.Message{
+ {
+ Role: "system",
+ Content: "You are Del, a code assistant. For any request that involves system interaction, respond using: TOOL_USE: tool_name {json}. Available tools: run_command, read_file, write_file, list_dir. Example: TOOL_USE: list_dir {\"path\": \".\"}",
+ },
+ },
}
}
// StartREPL launches an interactive session
func (d *Del) StartREPL(ctx context.Context) {
fmt.Printf("🎤 Del is ready with %s\n", d.aiProvider.Name())
+
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("🎤 You: ")
@@ -117,47 +127,53 @@ func (d *Del) StartREPL(ctx context.Context) {
return
}
- resp, err := d.aiProvider.Generate(ctx, input)
+ d.chatHistory = append(d.chatHistory, api.Message{Role: "user", Content: input})
+
+ response, err := d.aiProvider.Chat(ctx, d.chatHistory)
if err != nil {
fmt.Println("[error]", err)
continue
}
- output := d.handleToolCalls(ctx, resp)
+
+ d.chatHistory = append(d.chatHistory, api.Message{Role: "assistant", Content: response})
+ output := d.handleToolCalls(ctx, response)
fmt.Println(output)
}
}
-// handleToolCalls parses output for TOOL_USE and runs the associated tool.
+// handleToolCalls parses and runs embedded tool requests from the model output
func (d *Del) handleToolCalls(ctx context.Context, response string) string {
- toolUseRE := regexp.MustCompile(`(?m)^TOOL_USE:\s*(\w+)\s*(\{.*\})`)
- matches := toolUseRE.FindAllStringSubmatch(response, -1)
+ re := regexp.MustCompile(`(?s)TOOL_USE:\s*(\w+)\s*(\{.*?\})`)
+ matches := re.FindAllStringSubmatch(response, -1)
+ if len(matches) == 0 {
+ return response
+ }
- var results []string
+ var finalOutput strings.Builder
for _, match := range matches {
- name, jsonArgs := match[1], match[2]
- tool, ok := d.tools[name]
+ toolName := match[1]
+ argsJSON := match[2]
+
+ tool, ok := d.tools[toolName]
if !ok {
- results = append(results, fmt.Sprintf("[error] tool '%s' not found", name))
+ finalOutput.WriteString(fmt.Sprintf("[error] unknown tool: %s\n", toolName))
continue
}
var args map[string]interface{}
- if err := json.Unmarshal([]byte(jsonArgs), &args); err != nil {
- results = append(results, fmt.Sprintf("[error] invalid JSON args for tool '%s': %v", name, err))
+ if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
+ finalOutput.WriteString(fmt.Sprintf("[error] bad args: %v\n", err))
continue
}
result, err := tool.Handler(args)
if err != nil {
- results = append(results, fmt.Sprintf("[error] tool '%s' failed: %v", name, err))
+ finalOutput.WriteString(fmt.Sprintf("[tool error] %v\n", err))
continue
}
- results = append(results, fmt.Sprintf("[tool:%s] %v", name, result))
- }
- if len(results) == 0 {
- return response
+ outBytes, _ := json.MarshalIndent(result, "", " ")
+ finalOutput.WriteString(string(outBytes) + "\n")
}
-
- return strings.TrimSpace(response) + "\n\n" + strings.Join(results, "\n")
+ return finalOutput.String()
}
internal/del/provider.go
@@ -10,6 +10,7 @@ import (
type AIProvider interface {
Generate(ctx context.Context, prompt string) (string, error)
Name() string
+ Chat(ctx context.Context, history []api.Message) (string, error)
}
type OllamaProvider struct {
@@ -44,3 +45,20 @@ func (o *OllamaProvider) Generate(ctx context.Context, prompt string) (string, e
func (o *OllamaProvider) Name() string {
return fmt.Sprintf("Ollama (%s)", o.model)
}
+
+func (o *OllamaProvider) Chat(ctx context.Context, history []api.Message) (string, error) {
+ var response string
+ stream := false
+ err := o.client.Chat(ctx, &api.ChatRequest{
+ Model: o.model,
+ Messages: history,
+ Stream: &stream,
+ }, api.ChatResponseFunc(func(resp api.ChatResponse) error {
+ response += resp.Message.Content
+ return nil
+ }))
+ if err != nil {
+ return "", err
+ }
+ return response, nil
+}