UNPKG

@langchain/community

Version:
1 lines 14.7 kB
{"version":3,"file":"zep.cjs","names":["HumanMessage","SystemMessage","AIMessage","ChatMessage","ZepClient","NotFoundError","BaseMemory","Message"],"sources":["../../src/memory/zep.ts"],"sourcesContent":["import {\n Memory as BaseMemory,\n Message,\n NotFoundError,\n ZepClient,\n} from \"@getzep/zep-js\";\nimport {\n InputValues,\n OutputValues,\n MemoryVariables,\n getInputValue,\n getOutputValue,\n} from \"@langchain/core/memory\";\nimport {\n getBufferString,\n AIMessage,\n BaseMessage,\n ChatMessage,\n HumanMessage,\n SystemMessage,\n} from \"@langchain/core/messages\";\nimport { BaseChatMemory, BaseChatMemoryInput } from \"./chat_memory.js\";\n\n// Simple type for processed Zep memory data\ninterface ZepMemoryData {\n messages: Array<{\n role: string;\n content: string;\n }>;\n summary?: {\n content?: string;\n };\n}\n\n/**\n * Extracts summary from Zep memory and composes a system prompt.\n * @param memory - The memory object containing potential summary.\n * @returns A string containing the summary as a system prompt.\n */\nexport const zepMemoryContextToSystemPrompt = (memory: ZepMemoryData) => {\n let systemPrompt = \"\";\n\n // Extract summary, if present\n if (memory.summary && memory.summary?.content) {\n systemPrompt += memory.summary.content;\n }\n\n return systemPrompt;\n};\n\n/**\n * Condenses Zep memory context into a single HumanMessage.\n * This is particularly useful for models like Claude that have limitations with system messages\n * (e.g., Anthropic's Claude only supports one system message and doesn't support multiple user messages in a row).\n *\n * @param memory - The memory object containing conversation history.\n * @param humanPrefix - The prefix to use for human messages (default: \"Human\").\n * @param aiPrefix - The prefix to use for AI messages (default: \"AI\").\n * @returns A HumanMessage containing the condensed memory context.\n */\nexport const condenseZepMemoryIntoHumanMessage = (memory: ZepMemoryData) => {\n const systemPrompt = zepMemoryContextToSystemPrompt(memory);\n\n let concatMessages = \"\";\n\n // Add message history to the prompt, if present\n if (memory.messages) {\n concatMessages = memory.messages\n .map((msg) => `${msg.role}: ${msg.content}`)\n .join(\"\\n\");\n }\n\n return new HumanMessage(\n `${systemPrompt ? `${systemPrompt}\\n` : \"\"}${concatMessages}`\n );\n};\n\n/**\n * Converts Zep Memory to a list of BaseMessages, preserving the structure.\n * Creates a SystemMessage from summary and facts, and converts the rest of the messages\n * to their corresponding message types.\n *\n * @param memory - The memory object containing conversation history.\n * @param humanPrefix - The prefix to use for human messages (default: \"Human\").\n * @param aiPrefix - The prefix to use for AI messages (default: \"AI\").\n * @returns An array of BaseMessage objects representing the conversation history.\n */\nexport const zepMemoryToMessages = (\n memory: ZepMemoryData,\n humanPrefix = \"Human\",\n aiPrefix = \"AI\"\n) => {\n const systemPrompt = zepMemoryContextToSystemPrompt(memory);\n\n let messages: BaseMessage[] = systemPrompt\n ? [new SystemMessage(systemPrompt)]\n : [];\n\n if (memory && memory.messages) {\n messages = messages.concat(\n memory.messages\n .filter((m) => m.content)\n .map((message) => {\n const { content, role } = message;\n if (role === humanPrefix) {\n return new HumanMessage(content);\n } else if (role === aiPrefix) {\n return new AIMessage(content);\n } else {\n // default to generic ChatMessage\n return new ChatMessage(content, role);\n }\n })\n );\n }\n\n return messages;\n};\n\n/**\n * Interface defining the structure of the input data for the ZepMemory\n * class. It includes properties like humanPrefix, aiPrefix, memoryKey,\n * baseURL, sessionId, apiKey, and separateMessages.\n */\nexport interface ZepMemoryInput extends BaseChatMemoryInput {\n humanPrefix?: string;\n\n aiPrefix?: string;\n\n memoryKey?: string;\n\n baseURL: string;\n\n sessionId: string;\n\n // apiKey is optional.\n apiKey?: string;\n\n /**\n * Whether to return separate messages for chat history with a SystemMessage containing facts and summary,\n * or return a single HumanMessage with the entire memory context.\n * Defaults to true (preserving message types) for backward compatibility.\n *\n * Keep as true for models that fully support system messages.\n * Set to false for models like Claude that have limitations with system messages.\n */\n separateMessages?: boolean;\n}\n\n/**\n * Class used to manage the memory of a chat session, including loading\n * and saving the chat history, and clearing the memory when needed. It\n * uses the ZepClient to interact with the Zep service for managing the\n * chat session's memory.\n *\n * The class provides options for handling different LLM requirements:\n * - Use separateMessages=true (default) for models that fully support system messages\n * - Use separateMessages=false for models like Claude that have limitations with system messages\n *\n * @example\n * ```typescript\n * const sessionId = randomUUID();\n * const zepURL = \"http://your-zep-url\";\n *\n * // Initialize ZepMemory with session ID, base URL, and API key\n * const memory = new ZepMemory({\n * sessionId,\n * baseURL: zepURL,\n * apiKey: \"change_this_key\",\n * // Set to false for models like Claude that have limitations with system messages\n * // Defaults to true for backward compatibility\n * separateMessages: false,\n * });\n *\n * // Create a ChatOpenAI model instance with specific parameters\n * const model = new ChatOpenAI({\n * model: \"gpt-3.5-turbo\",\n * temperature: 0,\n * });\n *\n * // Create a ConversationChain with the model and memory\n * const chain = new ConversationChain({ llm: model, memory });\n *\n * // Example of calling the chain with an input\n * const res1 = await chain.call({ input: \"Hi! I'm Jim.\" });\n * console.log({ res1 });\n *\n * // Follow-up call to the chain to demonstrate memory usage\n * const res2 = await chain.call({ input: \"What did I just say my name was?\" });\n * console.log({ res2 });\n *\n * // Output the session ID and the current state of memory\n * console.log(\"Session ID: \", sessionId);\n * console.log(\"Memory: \", await memory.loadMemoryVariables({}));\n *\n * ```\n */\nexport class ZepMemory extends BaseChatMemory implements ZepMemoryInput {\n humanPrefix = \"Human\";\n\n aiPrefix = \"AI\";\n\n memoryKey = \"history\";\n\n baseURL: string;\n\n sessionId: string;\n\n zepClientPromise: Promise<ZepClient>;\n\n private readonly zepInitFailMsg = \"ZepClient is not initialized\";\n\n /**\n * Whether to return separate messages for chat history with a SystemMessage containing facts and summary,\n * or return a single HumanMessage with the entire memory context.\n * Defaults to true (preserving message types) for backward compatibility.\n *\n * Keep as true for models that fully support system messages.\n * Set to false for models like Claude that have limitations with system messages.\n */\n separateMessages: boolean;\n\n constructor(fields: ZepMemoryInput) {\n super({\n returnMessages: fields?.returnMessages ?? false,\n inputKey: fields?.inputKey,\n outputKey: fields?.outputKey,\n });\n\n this.humanPrefix = fields.humanPrefix ?? this.humanPrefix;\n this.aiPrefix = fields.aiPrefix ?? this.aiPrefix;\n this.memoryKey = fields.memoryKey ?? this.memoryKey;\n this.baseURL = fields.baseURL;\n this.sessionId = fields.sessionId;\n this.separateMessages = fields.separateMessages ?? true;\n this.zepClientPromise = ZepClient.init(this.baseURL, fields.apiKey);\n }\n\n get memoryKeys() {\n return [this.memoryKey];\n }\n\n /**\n * Method that retrieves the chat history from the Zep service and formats\n * it into a list of messages.\n * @param values Input values for the method.\n * @returns Promise that resolves with the chat history formatted into a list of messages.\n */\n async loadMemoryVariables(values: InputValues): Promise<MemoryVariables> {\n // use either lastN provided by developer or undefined to use the\n // server preset.\n\n // Wait for ZepClient to be initialized\n const zepClient = await this.zepClientPromise;\n if (!zepClient) {\n throw new Error(this.zepInitFailMsg);\n }\n\n const lastN = values.lastN ?? undefined;\n\n let memory: BaseMemory | null = null;\n try {\n memory = await zepClient.memory.getMemory(this.sessionId, lastN);\n } catch (error) {\n // eslint-disable-next-line no-instanceof/no-instanceof\n if (error instanceof NotFoundError) {\n const result = this.returnMessages\n ? { [this.memoryKey]: [] }\n : { [this.memoryKey]: \"\" };\n return result;\n } else {\n throw error;\n }\n }\n\n // Convert BaseMemory to ZepMemoryData\n const memoryData: ZepMemoryData = {\n messages:\n memory?.messages.map((msg: Message) => ({\n role: msg.role,\n content: msg.content,\n })) || [],\n summary: memory?.summary,\n };\n\n if (this.returnMessages) {\n return {\n [this.memoryKey]: this.separateMessages\n ? zepMemoryToMessages(memoryData, this.humanPrefix, this.aiPrefix)\n : [condenseZepMemoryIntoHumanMessage(memoryData)],\n };\n }\n return {\n [this.memoryKey]: this.separateMessages\n ? getBufferString(\n zepMemoryToMessages(memoryData, this.humanPrefix, this.aiPrefix),\n this.humanPrefix,\n this.aiPrefix\n )\n : condenseZepMemoryIntoHumanMessage(memoryData).content,\n };\n }\n\n /**\n * Method that saves the input and output messages to the Zep service.\n * @param inputValues Input messages to be saved.\n * @param outputValues Output messages to be saved.\n * @returns Promise that resolves when the messages have been saved.\n */\n async saveContext(\n inputValues: InputValues,\n outputValues: OutputValues\n ): Promise<void> {\n const input = getInputValue(inputValues, this.inputKey);\n const output = getOutputValue(outputValues, this.outputKey);\n\n // Create new Memory and Message instances\n const memory = new BaseMemory({\n messages: [\n new Message({\n role: this.humanPrefix,\n content: `${input}`,\n }),\n new Message({\n role: this.aiPrefix,\n content: `${output}`,\n }),\n ],\n });\n\n // Wait for ZepClient to be initialized\n const zepClient = await this.zepClientPromise;\n if (!zepClient) {\n throw new Error(this.zepInitFailMsg);\n }\n\n // Add the new memory to the session using the ZepClient\n if (this.sessionId) {\n try {\n await zepClient.memory.addMemory(this.sessionId, memory);\n } catch (error) {\n console.error(\"Error adding memory: \", error);\n }\n }\n\n // Call the superclass's saveContext method\n await super.saveContext(inputValues, outputValues);\n }\n\n /**\n * Method that deletes the chat history from the Zep service.\n * @returns Promise that resolves when the chat history has been deleted.\n */\n async clear(): Promise<void> {\n // Wait for ZepClient to be initialized\n const zepClient = await this.zepClientPromise;\n if (!zepClient) {\n throw new Error(this.zepInitFailMsg);\n }\n\n try {\n await zepClient.memory.deleteMemory(this.sessionId);\n } catch (error) {\n console.error(\"Error deleting session: \", error);\n }\n\n // Clear the superclass's chat history\n await super.clear();\n }\n}\n"],"mappings":"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"}