picoclaw/pkg/config/turn_profile.go

134 lines
3.5 KiB
Go
Raw Permalink Normal View History

package config
import (
"fmt"
"strings"
)
type TurnProfileMode string
const (
TurnProfileModeDefault TurnProfileMode = "default"
TurnProfileModeOff TurnProfileMode = "off"
TurnProfileModeCustom TurnProfileMode = "custom"
)
type TurnProfileConfig struct {
Enabled bool `json:"enabled"`
History TurnProfileBlock `json:"history,omitempty"`
SystemPrompt TurnProfileBlock `json:"system_prompt,omitempty"`
Skills TurnProfileBlock `json:"skills,omitempty"`
Tools TurnProfileBlock `json:"tools,omitempty"`
}
type TurnProfileBlock struct {
Mode TurnProfileMode `json:"mode,omitempty"`
Allow []string `json:"allow,omitempty"`
}
type EffectiveTurnProfile struct {
Enabled bool
HistoryMode TurnProfileMode
SystemPromptMode TurnProfileMode
SkillsMode TurnProfileMode
ToolsMode TurnProfileMode
AllowedSkills []string
AllowedTools []string
}
func (m TurnProfileMode) Effective() TurnProfileMode {
switch TurnProfileMode(strings.ToLower(strings.TrimSpace(string(m)))) {
case "", TurnProfileModeDefault:
return TurnProfileModeDefault
case TurnProfileModeOff:
return TurnProfileModeOff
case TurnProfileModeCustom:
return TurnProfileModeCustom
default:
return TurnProfileMode(strings.ToLower(strings.TrimSpace(string(m))))
}
}
func (d *AgentDefaults) ResolveTurnProfile() (EffectiveTurnProfile, bool, error) {
if d == nil {
return EffectiveTurnProfile{}, false, nil
}
profile := d.TurnProfile
if !profile.Enabled {
return EffectiveTurnProfile{}, false, nil
}
if err := validateTurnProfile(profile); err != nil {
return EffectiveTurnProfile{}, false, err
}
return EffectiveTurnProfile{
Enabled: true,
HistoryMode: profile.History.Mode.Effective(),
SystemPromptMode: profile.SystemPrompt.Mode.Effective(),
SkillsMode: profile.Skills.Mode.Effective(),
ToolsMode: profile.Tools.Mode.Effective(),
AllowedSkills: cleanStringList(profile.Skills.Allow),
AllowedTools: cleanStringList(profile.Tools.Allow),
}, true, nil
}
func (c *Config) ValidateTurnProfile() error {
if c == nil {
return nil
}
return validateTurnProfile(c.Agents.Defaults.TurnProfile)
}
func validateTurnProfile(profile TurnProfileConfig) error {
if !profile.Enabled {
return nil
}
if err := validateTurnProfileBlock("history", profile.History, false); err != nil {
return err
}
if err := validateTurnProfileBlock("system_prompt", profile.SystemPrompt, false); err != nil {
return err
}
if err := validateTurnProfileBlock("skills", profile.Skills, true); err != nil {
return err
}
if err := validateTurnProfileBlock("tools", profile.Tools, true); err != nil {
return err
}
return nil
}
func validateTurnProfileBlock(field string, block TurnProfileBlock, allowCustom bool) error {
mode := block.Mode.Effective()
switch mode {
case TurnProfileModeDefault, TurnProfileModeOff:
return nil
case TurnProfileModeCustom:
if allowCustom {
return nil
}
return fmt.Errorf("turn_profile.%s.mode custom is not supported in this version", field)
default:
return fmt.Errorf("turn_profile.%s.mode has unsupported mode %q", field, block.Mode)
}
}
func cleanStringList(values []string) []string {
if len(values) == 0 {
return nil
}
out := make([]string, 0, len(values))
seen := make(map[string]struct{}, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
out = append(out, value)
}
return out
}