ollie/cmd/olliesrv/internal/agent/skill_match.go

72 lines
1.4 KiB
Go

package agent
import (
"strings"
"sync"
"ollie/skills"
)
// Skill matching configuration.
const (
skillMatchThreshold = 0.35
skillMatchLimit = 3
)
var (
skillIndex *skills.Index
skillInitErr error
skillOnce sync.Once
)
// matchSkills finds skills relevant to the user's input and returns
// their content formatted for injection. Initializes the skill index
// on first call.
func matchSkills(input string) string {
skillOnce.Do(func() {
idx, err := skills.NewIndex(skills.DefaultModelDir(), skills.DefaultSkillDirs())
if err != nil {
skillInitErr = err
return
}
skillIndex = idx
})
if skillIndex == nil {
return ""
}
results, err := skillIndex.Match(input, skillMatchThreshold, skillMatchLimit)
if err != nil {
return ""
}
if len(results) == 0 {
return ""
}
var sb strings.Builder
sb.WriteString("<skills>\n")
for _, r := range results {
sb.WriteString("<skill name=\"")
sb.WriteString(r.Skill.Name)
sb.WriteString("\" relevance=\"")
sb.WriteString(formatScore(r.Score))
sb.WriteString("\">\n")
sb.WriteString(r.Skill.Content)
if !strings.HasSuffix(r.Skill.Content, "\n") {
sb.WriteString("\n")
}
sb.WriteString("</skill>\n")
}
sb.WriteString("</skills>\n")
return sb.String()
}
func formatScore(s float32) string {
pct := int(s * 100)
if pct > 99 {
pct = 99
}
return string([]byte{'0' + byte(pct/10), '0' + byte(pct%10), '%'})
}