feat: include user-specific daily notes in per-user memory context (#995)
This commit is contained in:
parent
4bea9128d2
commit
127db43e0e
2 changed files with 82 additions and 11 deletions
|
|
@ -448,9 +448,13 @@ func safePathSegment(v string) (string, bool) {
|
|||
}
|
||||
|
||||
// loadUserMemoryContext loads optional per-user memory content.
|
||||
// Supported lookup order:
|
||||
// 1) <workspace>/users/<channel>/<chatID>/MEMORY.md
|
||||
// 2) <workspace>/users/<chatID>/MEMORY.md
|
||||
// Supported lookup roots (in order):
|
||||
// 1) <workspace>/users/<channel>/<chatID>
|
||||
// 2) <workspace>/users/<chatID>
|
||||
//
|
||||
// For each root, it reads:
|
||||
// - MEMORY.md or memory/MEMORY.md (long-term)
|
||||
// - memory/YYYYMM/YYYYMMDD.md for recent daily notes (last 3 days)
|
||||
func (cb *ContextBuilder) loadUserMemoryContext(channel, chatID string) string {
|
||||
channelSeg, okChannel := safePathSegment(channel)
|
||||
chatSeg, okChat := safePathSegment(chatID)
|
||||
|
|
@ -458,21 +462,60 @@ func (cb *ContextBuilder) loadUserMemoryContext(channel, chatID string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
candidates := []string{}
|
||||
roots := []string{}
|
||||
if okChannel {
|
||||
candidates = append(candidates, filepath.Join(cb.workspace, "users", channelSeg, chatSeg, "MEMORY.md"))
|
||||
roots = append(roots, filepath.Join(cb.workspace, "users", channelSeg, chatSeg))
|
||||
}
|
||||
candidates = append(candidates, filepath.Join(cb.workspace, "users", chatSeg, "MEMORY.md"))
|
||||
roots = append(roots, filepath.Join(cb.workspace, "users", chatSeg))
|
||||
|
||||
for _, p := range candidates {
|
||||
if data, err := os.ReadFile(p); err == nil {
|
||||
for _, root := range roots {
|
||||
longTerm := ""
|
||||
for _, longTermPath := range []string{filepath.Join(root, "MEMORY.md"), filepath.Join(root, "memory", "MEMORY.md")} {
|
||||
if data, err := os.ReadFile(longTermPath); err == nil {
|
||||
text := strings.TrimSpace(string(data))
|
||||
if text != "" {
|
||||
return "## User-specific Memory\n\n" + text
|
||||
longTerm = text
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recentNotes := ""
|
||||
memoryDir := filepath.Join(root, "memory")
|
||||
for i := range 3 {
|
||||
date := time.Now().AddDate(0, 0, -i).Format("20060102")
|
||||
notePath := filepath.Join(memoryDir, date[:6], date+".md")
|
||||
if data, err := os.ReadFile(notePath); err == nil {
|
||||
text := strings.TrimSpace(string(data))
|
||||
if text != "" {
|
||||
if recentNotes != "" {
|
||||
recentNotes += "\n\n---\n\n"
|
||||
}
|
||||
recentNotes += text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if longTerm == "" && recentNotes == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("## User-specific Memory\n\n")
|
||||
if longTerm != "" {
|
||||
sb.WriteString("### Long-term Memory\n\n")
|
||||
sb.WriteString(longTerm)
|
||||
}
|
||||
if recentNotes != "" {
|
||||
if longTerm != "" {
|
||||
sb.WriteString("\n\n")
|
||||
}
|
||||
sb.WriteString("### Recent Daily Notes\n\n")
|
||||
sb.WriteString(recentNotes)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -701,3 +701,31 @@ func TestBuildMessages_UserSpecificMemoryIgnoredForInvalidChatID(t *testing.T) {
|
|||
t.Fatalf("expected no user memory section for invalid chat id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMessages_IncludesUserMemoryFromMemoryDirAndDailyNotes(t *testing.T) {
|
||||
today := time.Now().Format("20060102")
|
||||
tmpDir := setupWorkspace(t, map[string]string{
|
||||
"IDENTITY.md": "# Identity\nTest agent.",
|
||||
"users/u42/memory/MEMORY.md": "timezone: UTC",
|
||||
"users/u42/memory/" + today[:6] + "/" + today + ".md": "met a friend today",
|
||||
})
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
cb := NewContextBuilder(tmpDir)
|
||||
msgs := cb.BuildMessages(nil, "", "hello", nil, "webchat", "u42")
|
||||
if len(msgs) == 0 || msgs[0].Role != "system" {
|
||||
t.Fatalf("expected first system message")
|
||||
}
|
||||
if !strings.Contains(msgs[0].Content, "### Long-term Memory") {
|
||||
t.Fatalf("expected long-term user memory heading")
|
||||
}
|
||||
if !strings.Contains(msgs[0].Content, "timezone: UTC") {
|
||||
t.Fatalf("expected user long-term memory content")
|
||||
}
|
||||
if !strings.Contains(msgs[0].Content, "### Recent Daily Notes") {
|
||||
t.Fatalf("expected user daily notes heading")
|
||||
}
|
||||
if !strings.Contains(msgs[0].Content, "met a friend today") {
|
||||
t.Fatalf("expected user daily notes content")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue