Three compounding bugs prevented tool calling with llama.cpp:
- Stream parser set argsComplete on partial JSON (e.g. "{"), dropping
subsequent argument deltas — fix: use json.Valid to detect completeness
- Missing tool_choice default — llama.cpp needs explicit "auto" to
activate its GBNF grammar constraint; now set when tools are present
- Tool names in history used internal format (fs.ls) while definitions
used API format (fs_ls) — now re-sanitized in translateMessage
Additional changes:
- Disable SDK retries for local providers (500s are deterministic)
- Dynamic capability probing via /props (llama.cpp) and /api/show
(Ollama), replacing hardcoded model prefix list
- Engine respects forced arm ToolUse capability when router is active
- Bundled /init skill with Go template blocks, context-aware for local
vs cloud models, deduplication rules against CLAUDE.md
- Tool result compaction for local models — previous round results
replaced with size markers to stay within small context windows
- Text-only fallback when tool-parse errors occur on local models
- "text-only" TUI indicator when model lacks tool support
- Session ResetError for retry after stream failures
- AllowedTools per-turn filtering in engine buildRequest
45 lines
1.3 KiB
Go
45 lines
1.3 KiB
Go
package skill
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"text/template"
|
|
)
|
|
|
|
// TemplateData holds the variables available in skill body templates.
|
|
type TemplateData struct {
|
|
Args string // raw user arguments after the skill name
|
|
Cwd string // current working directory
|
|
ProjectRoot string // detected project root
|
|
Local bool // true if using a local provider (Ollama, llama.cpp)
|
|
}
|
|
|
|
// Render executes the skill body as a Go text/template with data.
|
|
// If the body contains no template directives and Args is non-empty,
|
|
// args are appended after the body with a blank line separator.
|
|
func (s *Skill) Render(data TemplateData) (string, error) {
|
|
t, err := template.New(s.Frontmatter.Name).Parse(s.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("skill %q: template parse error: %w", s.Frontmatter.Name, err)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := t.Execute(&buf, data); err != nil {
|
|
return "", fmt.Errorf("skill %q: template execute error: %w", s.Frontmatter.Name, err)
|
|
}
|
|
|
|
rendered := buf.String()
|
|
|
|
// If the body contained no template directives, the rendered output equals
|
|
// the original body. In that case, append args (if any) after a blank line.
|
|
if !strings.Contains(s.Body, "{{") && data.Args != "" {
|
|
if rendered == "" {
|
|
return data.Args, nil
|
|
}
|
|
return rendered + "\n" + data.Args, nil
|
|
}
|
|
|
|
return rendered, nil
|
|
}
|