mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 01:48:22 +08:00
3ca95b294d
* feat: integrate NOFXi agent into dev * Enhance NOFXi agent workflow and diagnostics
120 lines
3.3 KiB
Go
120 lines
3.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
//go:embed skills/*.json
|
|
var embeddedSkillDefinitions embed.FS
|
|
|
|
type SkillDefinition struct {
|
|
Name string `json:"name"`
|
|
Kind string `json:"kind"`
|
|
Domain string `json:"domain"`
|
|
Description string `json:"description"`
|
|
Intents []string `json:"intents,omitempty"`
|
|
Actions map[string]SkillActionDefinition `json:"actions,omitempty"`
|
|
ToolMapping map[string]string `json:"tool_mapping,omitempty"`
|
|
}
|
|
|
|
type SkillActionDefinition struct {
|
|
Description string `json:"description,omitempty"`
|
|
RequiredSlots []string `json:"required_slots,omitempty"`
|
|
OptionalSlots []string `json:"optional_slots,omitempty"`
|
|
NeedsConfirmation bool `json:"needs_confirmation,omitempty"`
|
|
}
|
|
|
|
var skillRegistry = mustLoadSkillRegistry()
|
|
|
|
func mustLoadSkillRegistry() map[string]SkillDefinition {
|
|
registry, err := loadSkillRegistry()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return registry
|
|
}
|
|
|
|
func loadSkillRegistry() (map[string]SkillDefinition, error) {
|
|
entries, err := embeddedSkillDefinitions.ReadDir("skills")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
registry := make(map[string]SkillDefinition, len(entries))
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
|
|
continue
|
|
}
|
|
raw, err := embeddedSkillDefinitions.ReadFile("skills/" + entry.Name())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var def SkillDefinition
|
|
if err := json.Unmarshal(raw, &def); err != nil {
|
|
return nil, fmt.Errorf("parse skill definition %s: %w", entry.Name(), err)
|
|
}
|
|
def = normalizeSkillDefinition(def)
|
|
if def.Name == "" {
|
|
return nil, fmt.Errorf("skill definition %s has empty name", entry.Name())
|
|
}
|
|
registry[def.Name] = def
|
|
}
|
|
return registry, nil
|
|
}
|
|
|
|
func normalizeSkillDefinition(def SkillDefinition) SkillDefinition {
|
|
def.Name = strings.TrimSpace(def.Name)
|
|
def.Kind = strings.TrimSpace(def.Kind)
|
|
def.Domain = strings.TrimSpace(def.Domain)
|
|
def.Description = strings.TrimSpace(def.Description)
|
|
def.Intents = cleanStringList(def.Intents)
|
|
|
|
if len(def.Actions) > 0 {
|
|
normalized := make(map[string]SkillActionDefinition, len(def.Actions))
|
|
for key, action := range def.Actions {
|
|
key = strings.TrimSpace(key)
|
|
if key == "" {
|
|
continue
|
|
}
|
|
action.Description = strings.TrimSpace(action.Description)
|
|
action.RequiredSlots = cleanStringList(action.RequiredSlots)
|
|
action.OptionalSlots = cleanStringList(action.OptionalSlots)
|
|
normalized[key] = action
|
|
}
|
|
def.Actions = normalized
|
|
}
|
|
|
|
if len(def.ToolMapping) > 0 {
|
|
normalized := make(map[string]string, len(def.ToolMapping))
|
|
for key, value := range def.ToolMapping {
|
|
key = strings.TrimSpace(key)
|
|
value = strings.TrimSpace(value)
|
|
if key == "" || value == "" {
|
|
continue
|
|
}
|
|
normalized[key] = value
|
|
}
|
|
def.ToolMapping = normalized
|
|
}
|
|
|
|
return def
|
|
}
|
|
|
|
func getSkillDefinition(name string) (SkillDefinition, bool) {
|
|
def, ok := skillRegistry[strings.TrimSpace(name)]
|
|
return def, ok
|
|
}
|
|
|
|
func listSkillNames() []string {
|
|
names := make([]string, 0, len(skillRegistry))
|
|
for name := range skillRegistry {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|