Files
ナムandCollective Developer 97e316f460 fix(memory): surface passive channel episodic recall (#1410)
* fix(memory): surface passive channel episodic recall

* fix(memory): expose episodic key topics

---------

Co-authored-by: Collective Developer <man@collective.dev>
2026-07-09 23:55:11 +07:00

98 lines
2.5 KiB
Go

package channelmemory
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"slices"
"strings"
"time"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
func (s *Service) itemFromExtracted(run *store.ChannelMemoryExtractionRun, extracted ExtractedItem) *store.ChannelMemoryExtractionItem {
topics, _ := json.Marshal(extracted.Topics)
entities, _ := json.Marshal(extracted.Entities)
hash := sha256.Sum256([]byte(strings.Join([]string{run.ChannelInstanceID.String(), run.HistoryKey, extracted.Type, extracted.Summary}, "\x00")))
itemID := hex.EncodeToString(hash[:])
return &store.ChannelMemoryExtractionItem{
RunID: run.ID,
ChannelInstanceID: run.ChannelInstanceID,
AgentID: run.AgentID,
UserID: run.UserID,
ItemHash: itemID,
ItemType: extracted.Type,
Summary: extracted.Summary,
Topics: topics,
Entities: entities,
Confidence: extracted.Confidence,
SourceID: "channel:" + itemID,
}
}
func eligibleHistoryKey(key string, cfg Config) bool {
if slices.Contains(cfg.ExcludeHistoryKeys, key) {
return false
}
if !cfg.GroupOnly {
return key != ""
}
k := strings.ToLower(key)
return key != "" && !strings.Contains(k, "dm") && !strings.Contains(k, "private")
}
func eligibleHistoryGroup(group store.PendingMessageGroup, cfg Config) bool {
if group.ParentHistoryKey != "" && slices.Contains(cfg.ExcludeHistoryKeys, group.ParentHistoryKey) {
return false
}
return eligibleHistoryKey(group.HistoryKey, cfg)
}
func messageSourceID(msg store.PendingMessage) string {
if msg.PlatformMsgID != "" {
return msg.PlatformMsgID
}
return msg.ID.String()
}
func decodeStrings(raw json.RawMessage) []string {
var out []string
_ = json.Unmarshal(raw, &out)
return out
}
func memoryKeyTopics(item *store.ChannelMemoryExtractionItem) []string {
if item == nil {
return nil
}
return mergeTopicLabels(decodeStrings(item.Topics), decodeStrings(item.Entities))
}
func mergeTopicLabels(groups ...[]string) []string {
seen := make(map[string]struct{})
var out []string
for _, group := range groups {
for _, value := range group {
label := strings.TrimSpace(value)
if label == "" {
continue
}
key := strings.ToLower(label)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, label)
}
}
return out
}
//go:fix inline
func timePtr(t time.Time) *time.Time { return &t }
func contains(values []string, v string) bool {
return slices.Contains(values, v)
}