mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Add CRUD to Saved Memory (#1929)
This commit is contained in:
parent
b90e80f509
commit
40d1badada
8 changed files with 189 additions and 127 deletions
|
|
@ -13,7 +13,7 @@ import { ToolManager } from "@/tools/toolManager";
|
|||
import { extractChatHistory } from "@/utils";
|
||||
import { Vault } from "obsidian";
|
||||
import { BrevilabsClient } from "./brevilabsClient";
|
||||
import { memoryTool } from "@/tools/memoryTools";
|
||||
import { updateMemoryTool } from "@/tools/memoryTools";
|
||||
import { AVAILABLE_TOOLS } from "@/components/chat-components/constants/tools";
|
||||
|
||||
type ToolCall = {
|
||||
|
|
@ -136,9 +136,9 @@ export class IntentAnalyzer {
|
|||
const cleanQuery = this.removeAtCommands(originalMessage);
|
||||
|
||||
processedToolCalls.push({
|
||||
tool: memoryTool,
|
||||
tool: updateMemoryTool,
|
||||
args: {
|
||||
memoryContent: cleanQuery,
|
||||
statement: cleanQuery,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -796,7 +796,7 @@ export const DEFAULT_SETTINGS: CopilotSettings = {
|
|||
"youtubeTranscription",
|
||||
"writeToFile",
|
||||
"replaceInFile",
|
||||
"memoryTool",
|
||||
"updateMemory",
|
||||
],
|
||||
reasoningEffort: DEFAULT_MODEL_SETTING.REASONING_EFFORT,
|
||||
verbosity: DEFAULT_MODEL_SETTING.VERBOSITY,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ describe("Composer Instructions - Integration Tests", () => {
|
|||
model: "gemini-2.5-flash-lite",
|
||||
generationConfig: {
|
||||
temperature: 0.1,
|
||||
maxOutputTokens: 1000,
|
||||
maxOutputTokens: 5000,
|
||||
},
|
||||
systemInstruction: DEFAULT_SYSTEM_PROMPT,
|
||||
safetySettings: [
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ jest.mock("@/utils", () => ({
|
|||
import { UserMemoryManager } from "./UserMemoryManager";
|
||||
import { App, TFile, Vault } from "obsidian";
|
||||
import { ChatMessage } from "@/types/message";
|
||||
import { logInfo, logError, logWarn } from "@/logger";
|
||||
import { logError, logWarn } from "@/logger";
|
||||
import { getSettings } from "@/settings/model";
|
||||
import { ensureFolderExists } from "@/utils";
|
||||
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
||||
|
|
@ -461,23 +461,20 @@ The conversation covered advanced features and included code examples.`,
|
|||
it("should skip saving when saved memory is disabled", async () => {
|
||||
mockSettings.enableSavedMemory = false;
|
||||
|
||||
const result = await userMemoryManager.addSavedMemory("Test memory content");
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(logWarn).toHaveBeenCalledWith(
|
||||
"[UserMemoryManager] Saved memory is disabled, skipping save"
|
||||
const result = await userMemoryManager.updateSavedMemory(
|
||||
"Test memory content",
|
||||
mockChatModel
|
||||
);
|
||||
|
||||
expect(result).toEqual({ error: "Saved memory is disabled, skipping save" });
|
||||
});
|
||||
|
||||
it("should skip saving when no content provided", async () => {
|
||||
mockSettings.enableSavedMemory = true;
|
||||
|
||||
const result = await userMemoryManager.addSavedMemory("");
|
||||
const result = await userMemoryManager.updateSavedMemory("", mockChatModel);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(logWarn).toHaveBeenCalledWith(
|
||||
"[UserMemoryManager] No content provided for saved memory"
|
||||
);
|
||||
expect(result).toEqual({ error: "No content provided for saved memory" });
|
||||
});
|
||||
|
||||
it("should save memory content to Saved Memories file", async () => {
|
||||
|
|
@ -493,8 +490,15 @@ The conversation covered advanced features and included code examples.`,
|
|||
const mockNewFile = createMockTFile("copilot/memory/Saved Memories.md");
|
||||
mockVault.create.mockResolvedValue(mockNewFile);
|
||||
|
||||
const result = await userMemoryManager.addSavedMemory(
|
||||
"Important user preference: I prefer dark mode"
|
||||
// Mock LLM merge result content
|
||||
const llmMergedContent = `- The user prefers concise responses`;
|
||||
(mockChatModel.invoke as jest.Mock).mockResolvedValue(
|
||||
new AIMessageChunk({ content: llmMergedContent })
|
||||
);
|
||||
|
||||
const result = await userMemoryManager.updateSavedMemory(
|
||||
"I prefer concise responses",
|
||||
mockChatModel
|
||||
);
|
||||
|
||||
// Verify folder creation was called
|
||||
|
|
@ -503,14 +507,13 @@ The conversation covered advanced features and included code examples.`,
|
|||
// Verify file creation was called with proper content
|
||||
expect(mockVault.create).toHaveBeenCalledWith(
|
||||
"copilot/memory/Saved Memories.md",
|
||||
expect.stringContaining("- Important user preference: I prefer dark mode")
|
||||
expect.stringContaining("- The user prefers concise responses")
|
||||
);
|
||||
|
||||
const createdContent = mockVault.create.mock.calls[0][1];
|
||||
expect(createdContent).not.toContain("**");
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(logInfo).toHaveBeenCalledWith("[UserMemoryManager] Saved memory added successfully");
|
||||
expect(result).toEqual({ content: llmMergedContent });
|
||||
});
|
||||
|
||||
it("should append to existing Saved Memories file", async () => {
|
||||
|
|
@ -529,7 +532,16 @@ The conversation covered advanced features and included code examples.`,
|
|||
mockVault.getAbstractFileByPath.mockReturnValue(mockMemoryFile);
|
||||
mockVault.read.mockResolvedValue(existingContent);
|
||||
|
||||
const result = await userMemoryManager.addSavedMemory("New important information");
|
||||
// Mock LLM to return merged full list
|
||||
const mergedContent = `- Previous memory content\n- Another important fact\n- New important information`;
|
||||
(mockChatModel.invoke as jest.Mock).mockResolvedValue(
|
||||
new AIMessageChunk({ content: mergedContent })
|
||||
);
|
||||
|
||||
const result = await userMemoryManager.updateSavedMemory(
|
||||
"New important information",
|
||||
mockChatModel
|
||||
);
|
||||
|
||||
// Verify file modification was called with appended content
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
|
|
@ -544,8 +556,7 @@ The conversation covered advanced features and included code examples.`,
|
|||
const modifiedContent = mockVault.modify.mock.calls[0][1];
|
||||
expect(modifiedContent).not.toContain("**");
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(logInfo).toHaveBeenCalledWith("[UserMemoryManager] Saved memory added successfully");
|
||||
expect(result).toEqual({ content: mergedContent });
|
||||
});
|
||||
|
||||
it("should handle errors during save operation", async () => {
|
||||
|
|
@ -554,13 +565,9 @@ The conversation covered advanced features and included code examples.`,
|
|||
// Mock ensureFolderExists to reject
|
||||
(ensureFolderExists as jest.Mock).mockRejectedValue(new Error("Folder creation failed"));
|
||||
|
||||
const result = await userMemoryManager.addSavedMemory("Test content");
|
||||
const result = await userMemoryManager.updateSavedMemory("Test content", mockChatModel);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(logError).toHaveBeenCalledWith(
|
||||
"[UserMemoryManager] Error saving memory:",
|
||||
expect.any(Error)
|
||||
);
|
||||
expect(result).toEqual({ error: "Error saving memory: Folder creation failed" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -80,34 +80,37 @@ export class UserMemoryManager {
|
|||
/**
|
||||
* Adds a saved memory that the user explicitly asked to remember
|
||||
*/
|
||||
async addSavedMemory(memoryContent: string): Promise<boolean> {
|
||||
async updateSavedMemory(
|
||||
query: string,
|
||||
chatModel: BaseChatModel
|
||||
): Promise<{ content?: string; error?: string }> {
|
||||
const settings = getSettings();
|
||||
|
||||
// Only proceed if saved memory is enabled
|
||||
if (!settings.enableSavedMemory) {
|
||||
logWarn("[UserMemoryManager] Saved memory is disabled, skipping save");
|
||||
return false;
|
||||
return { error: "Saved memory is disabled, skipping save" };
|
||||
}
|
||||
|
||||
if (!memoryContent || memoryContent.trim() === "") {
|
||||
logWarn("[UserMemoryManager] No content provided for saved memory");
|
||||
return false;
|
||||
if (!query || query.trim() === "") {
|
||||
return { error: "No content provided for saved memory" };
|
||||
}
|
||||
|
||||
if (!chatModel) {
|
||||
return { error: "No chat model available, skipping save" };
|
||||
}
|
||||
|
||||
try {
|
||||
// Ensure user memory folder exists
|
||||
await this.ensureMemoryFolderExists();
|
||||
// Create memory entry as a bullet point without timestamp metadata
|
||||
const memoryEntry = `- ${memoryContent.trim()}`;
|
||||
|
||||
// Add to saved memories file
|
||||
await this.addToSavedMemoryFile(this.getSavedMemoriesFilePath(), memoryEntry);
|
||||
|
||||
logInfo("[UserMemoryManager] Saved memory added successfully");
|
||||
return true;
|
||||
const result = await this.updateSavedMemoryFile(
|
||||
this.getSavedMemoriesFilePath(),
|
||||
query,
|
||||
chatModel
|
||||
);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logError("[UserMemoryManager] Error saving memory:", error);
|
||||
return false;
|
||||
return { error: "Error saving memory: " + error.message };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,30 +240,74 @@ export class UserMemoryManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Save content to saved memory file by appending new entry (no max limit)
|
||||
* Update content to saved memory file with ChatModel
|
||||
*/
|
||||
private async addToSavedMemoryFile(filePath: string, newMemoryEntry: string): Promise<void> {
|
||||
private async updateSavedMemoryFile(
|
||||
filePath: string,
|
||||
query: string,
|
||||
chatModel: BaseChatModel
|
||||
): Promise<{ content?: string; error?: string }> {
|
||||
const existingFile = this.app.vault.getAbstractFileByPath(filePath);
|
||||
|
||||
// Load existing saved memories (may be empty)
|
||||
const existingContent =
|
||||
existingFile instanceof TFile ? await this.app.vault.read(existingFile) : "";
|
||||
|
||||
// Fast path: if no model available for some reason, append a bullet safely
|
||||
if (!chatModel) {
|
||||
return { error: "No chat model available, skipping memory update" };
|
||||
}
|
||||
|
||||
// Ask the model to produce a deduplicated/merged/conflict-free full list
|
||||
const systemPrompt = `You maintain a user's long-term personal memory list as concise bullet points.
|
||||
|
||||
You task is to update the user's memory list with the new statement.
|
||||
Rules:
|
||||
- Keep only stable, evergreen facts or preferences that will help future conversations.
|
||||
- Remove duplicates and near-duplicates by merging them into one concise statement.
|
||||
- If the new statement conflicts with older ones, keep the most recent truth and remove obsolete/conflicting entries.
|
||||
- Prefer short, specific, and unambiguous phrasing.
|
||||
- Preserve the language used in the input memories.
|
||||
- Output only the memory content with each as a bullet point.
|
||||
|
||||
|
||||
# OUTPUT FORMAT
|
||||
Return the updated memory list with each as a bullet point.
|
||||
- memory item 1
|
||||
- memory item 2
|
||||
- memory item 3
|
||||
...
|
||||
`;
|
||||
|
||||
const humanPrompt = `<current_memories>
|
||||
${existingContent.trim()}
|
||||
</current_memories>
|
||||
|
||||
<new_statement>
|
||||
${query.trim()}
|
||||
</new_statement>
|
||||
`;
|
||||
|
||||
const messages_llm = [new SystemMessage(systemPrompt), new HumanMessage(humanPrompt)];
|
||||
|
||||
let updatedContent: string | null = null;
|
||||
try {
|
||||
const response = await chatModel.invoke(messages_llm);
|
||||
updatedContent = response.text ?? "";
|
||||
} catch (error) {
|
||||
return { error: "LLM call failed while updating saved memories: " + error.message };
|
||||
}
|
||||
if (updatedContent == null || updatedContent.trim() === "") {
|
||||
return { error: "Empty content returned from LLM" };
|
||||
}
|
||||
|
||||
if (existingFile instanceof TFile) {
|
||||
// Read existing content and append new entry
|
||||
const fileContent = await this.app.vault.read(existingFile);
|
||||
|
||||
let updatedContent: string;
|
||||
if (fileContent.trim() === "") {
|
||||
// Create new file with the entry
|
||||
updatedContent = `${newMemoryEntry}\n`;
|
||||
} else {
|
||||
// Append to existing content
|
||||
updatedContent = `${fileContent.trimEnd()}\n${newMemoryEntry}\n`;
|
||||
}
|
||||
|
||||
await this.app.vault.modify(existingFile, updatedContent);
|
||||
} else {
|
||||
// Create new file
|
||||
const initialContent = `${newMemoryEntry}\n`;
|
||||
await this.app.vault.create(filePath, initialContent);
|
||||
await this.app.vault.create(filePath, updatedContent);
|
||||
}
|
||||
|
||||
return { content: updatedContent };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -27,21 +27,22 @@ graph TD
|
|||
K --> L[Recent Memory Update Complete]
|
||||
|
||||
%% Saved Memory Flow
|
||||
M[User Explicitly Asks to Remember] --> N[memoryTool called]
|
||||
M[User Explicitly Asks to Remember] --> N[updateMemoryTool called]
|
||||
N --> O{enableSavedMemory?}
|
||||
O -->|Yes| P[Extract Memory Content]
|
||||
O -->|Yes| P[Ensure Memory Folder Exists]
|
||||
O -->|No| Z2[Skip Saved Memory]
|
||||
P --> Q[Format as Bullet Point]
|
||||
Q --> R[Append to Saved Memories.md]
|
||||
R --> S[Saved Memory Complete]
|
||||
P --> Q[Load Saved Memories]
|
||||
Q --> R[Generate Merged Bullet List via LLM]
|
||||
R --> S[Overwrite Saved Memories.md]
|
||||
S --> T_saved[Saved Memory Complete]
|
||||
|
||||
%% Memory Retrieval
|
||||
T[LLM Request] --> U[getUserMemoryPrompt called]
|
||||
U --> V[Load Recent Conversations]
|
||||
U --> W[Load Saved Memories]
|
||||
V --> X[Combine Memory Sections]
|
||||
W --> X
|
||||
X --> Y[Return Memory Context for LLM]
|
||||
U[LLM Request] --> V[getUserMemoryPrompt called]
|
||||
V --> W[Load Recent Conversations]
|
||||
V --> X[Load Saved Memories]
|
||||
W --> Y[Combine Memory Sections]
|
||||
X --> Y
|
||||
Y --> Z[Return Memory Context for LLM]
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
|
@ -57,7 +58,7 @@ graph TD
|
|||
|
||||
**Saved Memories:**
|
||||
|
||||
- **Trigger**: When user explicitly asks to remember something during chat and `memoryTool` is called
|
||||
- **Trigger**: When user explicitly asks to remember something during chat and `updateMemoryTool` is called
|
||||
- **Guard**: Only if `enableSavedMemory` setting is enabled
|
||||
- **Immediate**: Saves directly to file when invoked
|
||||
- **User notification**: Shows success/failure notice to user
|
||||
|
|
@ -76,19 +77,23 @@ graph TD
|
|||
|
||||
### Saved Memories:
|
||||
|
||||
- **When**: User explicitly asks to remember something via `memoryTool`
|
||||
- **When**: User explicitly asks to remember something via `updateMemoryTool`
|
||||
- **Retention policy**: No limit - memories persist until manually deleted
|
||||
- **Content**:
|
||||
- Raw user-specified information to remember
|
||||
- Personal facts, preferences, important decisions, or context
|
||||
- **Format**: Simple bullet-point list in markdown
|
||||
- LLM-normalized bullet list that deduplicates and merges related statements
|
||||
- Conflict resolution keeps the most recent truth and removes obsolete entries
|
||||
- Preserves the language of the source statements
|
||||
- **Format**: Bullet-point list in markdown maintained by the LLM
|
||||
- **Storage**: `Saved Memories.md` in the configured memory folder
|
||||
- **File handling**: Appends new memories to existing file, creates if doesn't exist
|
||||
- **File handling**: Loads existing memories, asks the LLM to produce a fully merged list, and overwrites the file (creates if it doesn't exist)
|
||||
|
||||
### Message Processing Features:
|
||||
|
||||
- **Conversation Titles**: LLM-extracted titles that capture main user intent (2-8 words)
|
||||
- **Conversation Summaries**: AI-generated 2-3 sentence summaries with key details and conclusions
|
||||
- **Saved Memory Normalization**: LLM produces a concise, deduplicated bullet list for long-term memories while resolving conflicts
|
||||
- **Memory Tool Integration**: Explicit memory saving via natural language commands
|
||||
- **Robust JSON Parsing**: Handles JSON responses wrapped in code blocks (common with Gemini and other LLMs) with fallback to plain JSON extraction
|
||||
- **Language-aware**: Uses the same language as the conversation for titles and summaries
|
||||
|
|
@ -114,6 +119,7 @@ graph TD
|
|||
- Fallback mechanisms for AI processing failures
|
||||
- Graceful handling of missing files and folders
|
||||
- User notifications for saved memory operations (success/failure)
|
||||
- Saved memory writes fall back with descriptive error messages if LLM responses are empty or unavailable
|
||||
- Robust JSON extraction from LLM responses with multiple parsing strategies (code blocks, inline JSON, fallback to raw content)
|
||||
- Race condition protection for concurrent memory updates
|
||||
|
||||
|
|
@ -133,10 +139,10 @@ The memory system behaves differently depending on which chat mode is active:
|
|||
### Agent Mode (Autonomous Agent)
|
||||
|
||||
- **Memory Retrieval**: ✅ Full access to both Recent Conversations and Saved Memories via system prompt
|
||||
- **Memory Saving**: ✅ Direct access to `memoryTool` through XML-based tool calling
|
||||
- **Memory Saving**: ✅ Direct access to `updateMemoryTool` through XML-based tool calling
|
||||
- **Behavior**:
|
||||
- AI autonomously decides when to save memories based on user requests
|
||||
- Uses XML format: `<use_tool><name>memoryTool</name><memoryContent>...</memoryContent></use_tool>`
|
||||
- Uses XML format: `<use_tool><name>updateMemoryTool</name><memoryContent>...</memoryContent></use_tool>`
|
||||
- Can reason step-by-step about whether something should be remembered
|
||||
- Shows user notifications when memories are saved
|
||||
- Access controlled by tool enablement settings (`autonomousAgentEnabledToolIds`)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Vault } from "obsidian";
|
|||
import { replaceInFileTool, writeToFileTool } from "./ComposerTools";
|
||||
import { createGetFileTreeTool } from "./FileTreeTools";
|
||||
import { createGetTagListTool } from "./TagTools";
|
||||
import { memoryTool } from "./memoryTools";
|
||||
import { updateMemoryTool } from "./memoryTools";
|
||||
import { readNoteTool } from "./NoteTools";
|
||||
import { localSearchTool, webSearchTool } from "./SearchTools";
|
||||
import {
|
||||
|
|
@ -373,24 +373,24 @@ export function registerMemoryTool(): void {
|
|||
const registry = ToolRegistry.getInstance();
|
||||
|
||||
registry.register({
|
||||
tool: memoryTool,
|
||||
tool: updateMemoryTool,
|
||||
metadata: {
|
||||
id: "memoryTool",
|
||||
displayName: "Save Memory",
|
||||
description: "Save information to user memory when explicitly asked to remember something",
|
||||
id: "updateMemory",
|
||||
displayName: "Update Memory",
|
||||
description:
|
||||
"Save information to user memory when the user explicitly asks to remember something or update the memory",
|
||||
category: "memory",
|
||||
copilotCommands: ["@memory"],
|
||||
isAlwaysEnabled: true,
|
||||
customPromptInstructions: `For memoryTool:
|
||||
- Use ONLY when the user explicitly asks you to remember something (phrases like "remember that", "don't forget", etc.)
|
||||
- DO NOT use for general information - only for personal facts, preferences, or specific things the user wants stored
|
||||
- Extract the key information to remember from the user's message
|
||||
customPromptInstructions: `For updateMemory:
|
||||
- Use this tool to update the memory when the user explicitly asks to update the memory
|
||||
- DO NOT use for general information - only for personal facts, preferences, or specific things the user wants stored
|
||||
|
||||
Example usage:
|
||||
<use_tool>
|
||||
<name>memoryTool</name>
|
||||
<memoryContent>User's favorite programming language is Python and they prefer functional programming style</memoryContent>
|
||||
</use_tool>`,
|
||||
Example usage:
|
||||
<use_tool>
|
||||
<name>updateMemory</name>
|
||||
<statement>I'm studying Japanese and I'm preparing for JLPT N3</statement>
|
||||
</use_tool>`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -409,13 +409,13 @@ export function initializeBuiltinTools(vault?: Vault): void {
|
|||
// Only reinitialize if tools have changed or vault/memory status has changed
|
||||
const hasFileTree = registry.getToolMetadata("getFileTree") !== undefined;
|
||||
const shouldHaveFileTree = vault !== undefined;
|
||||
const hasMemoryTool = registry.getToolMetadata("memoryTool") !== undefined;
|
||||
const hasUpdateMemoryTool = registry.getToolMetadata("updateMemory") !== undefined;
|
||||
const shouldHaveMemoryTool = settings.enableSavedMemory;
|
||||
|
||||
if (
|
||||
registry.getAllTools().length === 0 ||
|
||||
hasFileTree !== shouldHaveFileTree ||
|
||||
hasMemoryTool !== shouldHaveMemoryTool
|
||||
hasUpdateMemoryTool !== shouldHaveMemoryTool
|
||||
) {
|
||||
// Clear any existing tools
|
||||
registry.clear();
|
||||
|
|
|
|||
|
|
@ -2,49 +2,51 @@ import { z } from "zod";
|
|||
import { createTool, SimpleTool } from "./SimpleTool";
|
||||
import { UserMemoryManager } from "@/memory/UserMemoryManager";
|
||||
import { logError } from "@/logger";
|
||||
import ChatModelManager from "@/LLMProviders/chatModelManager";
|
||||
|
||||
// Define Zod schema for memoryTool
|
||||
// Define Zod schema for updateMemoryTool
|
||||
const memorySchema = z.object({
|
||||
memoryContent: z
|
||||
statement: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe(
|
||||
"The content to save to user's memory (information the user explicitly asked to remember)"
|
||||
),
|
||||
.describe("The user statement for explicitly updating saved memories"),
|
||||
});
|
||||
|
||||
/**
|
||||
* Memory tool for saving information that the user explicitly asks the assistant to remember
|
||||
*/
|
||||
export const memoryTool: SimpleTool<typeof memorySchema, { success: boolean; message: string }> =
|
||||
createTool({
|
||||
name: "memoryTool",
|
||||
description:
|
||||
"Save information to user memory when the user explicitly asks to remember something",
|
||||
schema: memorySchema,
|
||||
handler: async ({ memoryContent }) => {
|
||||
try {
|
||||
const memoryManager = new UserMemoryManager(app);
|
||||
const success = await memoryManager.addSavedMemory(memoryContent);
|
||||
if (!success) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to save memory: ${memoryContent}`,
|
||||
};
|
||||
}
|
||||
const memoryFilePath = memoryManager.getSavedMemoriesFilePath();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Memory saved successfully into ${memoryFilePath}: ${memoryContent}`,
|
||||
};
|
||||
} catch (error) {
|
||||
logError("[memoryTool] Error saving memory:", error);
|
||||
export const updateMemoryTool: SimpleTool<
|
||||
typeof memorySchema,
|
||||
{ success: boolean; message: string }
|
||||
> = createTool({
|
||||
name: "updateMemory",
|
||||
description: "Update the user memory when the user explicitly asks to update the memory",
|
||||
schema: memorySchema,
|
||||
handler: async ({ statement }) => {
|
||||
try {
|
||||
const memoryManager = new UserMemoryManager(app);
|
||||
const chatModel = ChatModelManager.getInstance().getChatModel();
|
||||
const result = await memoryManager.updateSavedMemory(statement, chatModel);
|
||||
|
||||
if (result.error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to save memory: ${error.message}`,
|
||||
message: result.error,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const memoryFilePath = memoryManager.getSavedMemoriesFilePath();
|
||||
return {
|
||||
success: true,
|
||||
message: `Memory updated successfully into ${memoryFilePath}: ${result.content}`,
|
||||
};
|
||||
} catch (error) {
|
||||
logError("[updateMemoryTool] Error updating memory:", error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to save memory: ${error.message}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue