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
162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"hash/fnv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// PersistentPreference is a durable user instruction shown in the UI and
|
|
// injected into the agent context for future conversations.
|
|
type PersistentPreference struct {
|
|
ID string `json:"id"`
|
|
Text string `json:"text"`
|
|
CreatedAt string `json:"created_at,omitempty"`
|
|
}
|
|
|
|
func NewPersistentPreference(text string) (PersistentPreference, error) {
|
|
text = strings.TrimSpace(text)
|
|
if text == "" {
|
|
return PersistentPreference{}, fmt.Errorf("text required")
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
return PersistentPreference{
|
|
ID: now.Format("20060102150405.000000000"),
|
|
Text: text,
|
|
CreatedAt: now.Format(time.RFC3339),
|
|
}, nil
|
|
}
|
|
|
|
// SessionUserIDFromKey maps a stable user key (for example a UUID string from
|
|
// auth) to the int64 session id expected by the current agent implementation.
|
|
func SessionUserIDFromKey(userKey string) int64 {
|
|
if strings.TrimSpace(userKey) == "" {
|
|
return 1
|
|
}
|
|
h := fnv.New64a()
|
|
_, _ = h.Write([]byte(userKey))
|
|
sum := h.Sum64() & 0x7fffffffffffffff
|
|
if sum == 0 {
|
|
return 1
|
|
}
|
|
return int64(sum)
|
|
}
|
|
|
|
func PreferencesConfigKey(userID int64) string {
|
|
return fmt.Sprintf("agent_preferences_%d", userID)
|
|
}
|
|
|
|
func (a *Agent) getPersistentPreferences(userID int64) []PersistentPreference {
|
|
if a.store == nil {
|
|
return nil
|
|
}
|
|
|
|
raw, err := a.store.GetSystemConfig(PreferencesConfigKey(userID))
|
|
if err != nil || strings.TrimSpace(raw) == "" {
|
|
return nil
|
|
}
|
|
|
|
var prefs []PersistentPreference
|
|
if err := json.Unmarshal([]byte(raw), &prefs); err != nil {
|
|
a.logger.Warn("failed to parse persistent preferences", "error", err, "user_id", userID)
|
|
return nil
|
|
}
|
|
return prefs
|
|
}
|
|
|
|
func (a *Agent) savePersistentPreferences(userID int64, prefs []PersistentPreference) error {
|
|
if a.store == nil {
|
|
return fmt.Errorf("store unavailable")
|
|
}
|
|
data, err := json.Marshal(prefs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return a.store.SetSystemConfig(PreferencesConfigKey(userID), string(data))
|
|
}
|
|
|
|
func (a *Agent) addPersistentPreference(userID int64, text string) ([]PersistentPreference, PersistentPreference, error) {
|
|
created, err := NewPersistentPreference(text)
|
|
if err != nil {
|
|
return nil, PersistentPreference{}, err
|
|
}
|
|
prefs := a.getPersistentPreferences(userID)
|
|
prefs = append([]PersistentPreference{created}, prefs...)
|
|
if len(prefs) > 20 {
|
|
prefs = prefs[:20]
|
|
}
|
|
if err := a.savePersistentPreferences(userID, prefs); err != nil {
|
|
return nil, PersistentPreference{}, err
|
|
}
|
|
return prefs, created, nil
|
|
}
|
|
|
|
func (a *Agent) updatePersistentPreference(userID int64, match, replacement string) ([]PersistentPreference, *PersistentPreference, error) {
|
|
match = strings.TrimSpace(match)
|
|
replacement = strings.TrimSpace(replacement)
|
|
if match == "" || replacement == "" {
|
|
return nil, nil, fmt.Errorf("match and replacement are required")
|
|
}
|
|
|
|
prefs := a.getPersistentPreferences(userID)
|
|
for i := range prefs {
|
|
if prefs[i].ID == match || strings.Contains(strings.ToLower(prefs[i].Text), strings.ToLower(match)) {
|
|
prefs[i].Text = replacement
|
|
if err := a.savePersistentPreferences(userID, prefs); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return prefs, &prefs[i], nil
|
|
}
|
|
}
|
|
return prefs, nil, fmt.Errorf("preference not found")
|
|
}
|
|
|
|
func (a *Agent) deletePersistentPreference(userID int64, match string) ([]PersistentPreference, *PersistentPreference, error) {
|
|
match = strings.TrimSpace(match)
|
|
if match == "" {
|
|
return nil, nil, fmt.Errorf("match required")
|
|
}
|
|
|
|
prefs := a.getPersistentPreferences(userID)
|
|
filtered := make([]PersistentPreference, 0, len(prefs))
|
|
var removed *PersistentPreference
|
|
for i := range prefs {
|
|
p := prefs[i]
|
|
if removed == nil && (p.ID == match || strings.Contains(strings.ToLower(p.Text), strings.ToLower(match))) {
|
|
cp := p
|
|
removed = &cp
|
|
continue
|
|
}
|
|
filtered = append(filtered, p)
|
|
}
|
|
if removed == nil {
|
|
return prefs, nil, fmt.Errorf("preference not found")
|
|
}
|
|
if err := a.savePersistentPreferences(userID, filtered); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return filtered, removed, nil
|
|
}
|
|
|
|
func (a *Agent) buildPersistentPreferencesContext(userID int64) string {
|
|
prefs := a.getPersistentPreferences(userID)
|
|
if len(prefs) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.WriteString("[Persistent User Preferences - follow unless the user explicitly overrides them]\n")
|
|
for _, pref := range prefs {
|
|
if strings.TrimSpace(pref.Text) == "" {
|
|
continue
|
|
}
|
|
sb.WriteString("- ")
|
|
sb.WriteString(pref.Text)
|
|
sb.WriteString("\n")
|
|
}
|
|
return strings.TrimSpace(sb.String())
|
|
}
|