Add structured memory system for persistent agent context
Implements `StructuredMemoryManager` to track conversation summaries, user preferences, and learned facts across sessions. Includes: - Configurable storage limits with automatic enforcement - Heuristic extraction of preferences and facts from messages - Memory context injection into system prompts - Full test coverage for all manager operations
This commit is contained in:
+35
-9
@@ -59,16 +59,22 @@ const mockSettings: PluginSettings = {
|
||||
minNoteLength: 50,
|
||||
maxNoteLength: 8000,
|
||||
tagPromptTemplate: 'Tags: {{content}}',
|
||||
dryRun: false,
|
||||
targetFolder: '',
|
||||
normalizeTags: true,
|
||||
dryRun: false,
|
||||
targetFolder: '',
|
||||
normalizeTags: true,
|
||||
},
|
||||
autoLinkConfig: {
|
||||
enabled: false,
|
||||
maxLinksPerNote: 3,
|
||||
similarityThreshold: 0.6,
|
||||
targetFolder: '',
|
||||
dryRun: false,
|
||||
targetFolder: '',
|
||||
dryRun: false,
|
||||
},
|
||||
structuredMemoryConfig: {
|
||||
enabled: true,
|
||||
maxSummaries: 10,
|
||||
maxPreferences: 20,
|
||||
maxFacts: 50,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -354,7 +360,17 @@ describe('ChatView', () => {
|
||||
view['inputEl'] = document.createElement('textarea');
|
||||
(view['inputEl'] as HTMLTextAreaElement).value = 'search query';
|
||||
|
||||
const searchSpy = jest.spyOn(view["noteContextBuilder"], "buildContext").mockResolvedValue({ explicitMentions: [], openNote: undefined, selectedText: undefined, backlinks: [], outlinks: [], relatedNotes: [], searchResults: [] });
|
||||
const searchSpy = jest
|
||||
.spyOn(view['noteContextBuilder'], 'buildContext')
|
||||
.mockResolvedValue({
|
||||
explicitMentions: [],
|
||||
openNote: undefined,
|
||||
selectedText: undefined,
|
||||
backlinks: [],
|
||||
outlinks: [],
|
||||
relatedNotes: [],
|
||||
searchResults: [],
|
||||
});
|
||||
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||
(async function* () {
|
||||
yield { role: 'assistant', content: 'response' };
|
||||
@@ -363,7 +379,7 @@ describe('ChatView', () => {
|
||||
|
||||
await (view as any).handleUserInput('search query');
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith("search query", 3, expect.any(Object)); // Should use DEFAULT_VAULT_SEARCH_LIMIT
|
||||
expect(searchSpy).toHaveBeenCalledWith('search query', 3, expect.any(Object)); // Should use DEFAULT_VAULT_SEARCH_LIMIT
|
||||
expect(chatSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -799,7 +815,17 @@ describe('ChatView', () => {
|
||||
view['inputEl'] = document.createElement('textarea');
|
||||
(view['inputEl'] as HTMLTextAreaElement).value = 'search query';
|
||||
|
||||
const searchSpy = jest.spyOn(view["noteContextBuilder"], "buildContext").mockResolvedValue({ explicitMentions: [], openNote: undefined, selectedText: undefined, backlinks: [], outlinks: [], relatedNotes: [], searchResults: [] });
|
||||
const searchSpy = jest
|
||||
.spyOn(view['noteContextBuilder'], 'buildContext')
|
||||
.mockResolvedValue({
|
||||
explicitMentions: [],
|
||||
openNote: undefined,
|
||||
selectedText: undefined,
|
||||
backlinks: [],
|
||||
outlinks: [],
|
||||
relatedNotes: [],
|
||||
searchResults: [],
|
||||
});
|
||||
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||
(async function* () {
|
||||
yield { role: 'assistant', content: 'response' };
|
||||
@@ -808,7 +834,7 @@ describe('ChatView', () => {
|
||||
|
||||
await (view as any).handleUserInput('search query');
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith("search query", 3, expect.any(Object)); // Should use DEFAULT_VAULT_SEARCH_LIMIT
|
||||
expect(searchSpy).toHaveBeenCalledWith('search query', 3, expect.any(Object)); // Should use DEFAULT_VAULT_SEARCH_LIMIT
|
||||
expect(chatSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user