@langchain/community
Version:
Third-party integrations for LangChain.js
212 lines (211 loc) • 8.81 kB
JavaScript
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
import { chat_memory_exports } from "./chat_memory.js";
import { AIMessage, ChatMessage, HumanMessage, SystemMessage, getBufferString } from "@langchain/core/messages";
import { getInputValue, getOutputValue } from "@langchain/core/memory";
import { Memory, Message, NotFoundError, ZepClient } from "@getzep/zep-js";
//#region src/memory/zep.ts
var zep_exports = /* @__PURE__ */ __exportAll({
ZepMemory: () => ZepMemory,
condenseZepMemoryIntoHumanMessage: () => condenseZepMemoryIntoHumanMessage,
zepMemoryContextToSystemPrompt: () => zepMemoryContextToSystemPrompt,
zepMemoryToMessages: () => zepMemoryToMessages
});
/**
* Extracts summary from Zep memory and composes a system prompt.
* @param memory - The memory object containing potential summary.
* @returns A string containing the summary as a system prompt.
*/
const zepMemoryContextToSystemPrompt = (memory) => {
let systemPrompt = "";
if (memory.summary && memory.summary?.content) systemPrompt += memory.summary.content;
return systemPrompt;
};
/**
* Condenses Zep memory context into a single HumanMessage.
* This is particularly useful for models like Claude that have limitations with system messages
* (e.g., Anthropic's Claude only supports one system message and doesn't support multiple user messages in a row).
*
* @param memory - The memory object containing conversation history.
* @param humanPrefix - The prefix to use for human messages (default: "Human").
* @param aiPrefix - The prefix to use for AI messages (default: "AI").
* @returns A HumanMessage containing the condensed memory context.
*/
const condenseZepMemoryIntoHumanMessage = (memory) => {
const systemPrompt = zepMemoryContextToSystemPrompt(memory);
let concatMessages = "";
if (memory.messages) concatMessages = memory.messages.map((msg) => `${msg.role}: ${msg.content}`).join("\n");
return new HumanMessage(`${systemPrompt ? `${systemPrompt}\n` : ""}${concatMessages}`);
};
/**
* Converts Zep Memory to a list of BaseMessages, preserving the structure.
* Creates a SystemMessage from summary and facts, and converts the rest of the messages
* to their corresponding message types.
*
* @param memory - The memory object containing conversation history.
* @param humanPrefix - The prefix to use for human messages (default: "Human").
* @param aiPrefix - The prefix to use for AI messages (default: "AI").
* @returns An array of BaseMessage objects representing the conversation history.
*/
const zepMemoryToMessages = (memory, humanPrefix = "Human", aiPrefix = "AI") => {
const systemPrompt = zepMemoryContextToSystemPrompt(memory);
let messages = systemPrompt ? [new SystemMessage(systemPrompt)] : [];
if (memory && memory.messages) messages = messages.concat(memory.messages.filter((m) => m.content).map((message) => {
const { content, role } = message;
if (role === humanPrefix) return new HumanMessage(content);
else if (role === aiPrefix) return new AIMessage(content);
else return new ChatMessage(content, role);
}));
return messages;
};
/**
* Class used to manage the memory of a chat session, including loading
* and saving the chat history, and clearing the memory when needed. It
* uses the ZepClient to interact with the Zep service for managing the
* chat session's memory.
*
* The class provides options for handling different LLM requirements:
* - Use separateMessages=true (default) for models that fully support system messages
* - Use separateMessages=false for models like Claude that have limitations with system messages
*
* @example
* ```typescript
* const sessionId = randomUUID();
* const zepURL = "http://your-zep-url";
*
* // Initialize ZepMemory with session ID, base URL, and API key
* const memory = new ZepMemory({
* sessionId,
* baseURL: zepURL,
* apiKey: "change_this_key",
* // Set to false for models like Claude that have limitations with system messages
* // Defaults to true for backward compatibility
* separateMessages: false,
* });
*
* // Create a ChatOpenAI model instance with specific parameters
* const model = new ChatOpenAI({
* model: "gpt-3.5-turbo",
* temperature: 0,
* });
*
* // Create a ConversationChain with the model and memory
* const chain = new ConversationChain({ llm: model, memory });
*
* // Example of calling the chain with an input
* const res1 = await chain.call({ input: "Hi! I'm Jim." });
* console.log({ res1 });
*
* // Follow-up call to the chain to demonstrate memory usage
* const res2 = await chain.call({ input: "What did I just say my name was?" });
* console.log({ res2 });
*
* // Output the session ID and the current state of memory
* console.log("Session ID: ", sessionId);
* console.log("Memory: ", await memory.loadMemoryVariables({}));
*
* ```
*/
var ZepMemory = class extends chat_memory_exports.BaseChatMemory {
humanPrefix = "Human";
aiPrefix = "AI";
memoryKey = "history";
baseURL;
sessionId;
zepClientPromise;
zepInitFailMsg = "ZepClient is not initialized";
/**
* Whether to return separate messages for chat history with a SystemMessage containing facts and summary,
* or return a single HumanMessage with the entire memory context.
* Defaults to true (preserving message types) for backward compatibility.
*
* Keep as true for models that fully support system messages.
* Set to false for models like Claude that have limitations with system messages.
*/
separateMessages;
constructor(fields) {
super({
returnMessages: fields?.returnMessages ?? false,
inputKey: fields?.inputKey,
outputKey: fields?.outputKey
});
this.humanPrefix = fields.humanPrefix ?? this.humanPrefix;
this.aiPrefix = fields.aiPrefix ?? this.aiPrefix;
this.memoryKey = fields.memoryKey ?? this.memoryKey;
this.baseURL = fields.baseURL;
this.sessionId = fields.sessionId;
this.separateMessages = fields.separateMessages ?? true;
this.zepClientPromise = ZepClient.init(this.baseURL, fields.apiKey);
}
get memoryKeys() {
return [this.memoryKey];
}
/**
* Method that retrieves the chat history from the Zep service and formats
* it into a list of messages.
* @param values Input values for the method.
* @returns Promise that resolves with the chat history formatted into a list of messages.
*/
async loadMemoryVariables(values) {
const zepClient = await this.zepClientPromise;
if (!zepClient) throw new Error(this.zepInitFailMsg);
const lastN = values.lastN ?? void 0;
let memory = null;
try {
memory = await zepClient.memory.getMemory(this.sessionId, lastN);
} catch (error) {
if (error instanceof NotFoundError) return this.returnMessages ? { [this.memoryKey]: [] } : { [this.memoryKey]: "" };
else throw error;
}
const memoryData = {
messages: memory?.messages.map((msg) => ({
role: msg.role,
content: msg.content
})) || [],
summary: memory?.summary
};
if (this.returnMessages) return { [this.memoryKey]: this.separateMessages ? zepMemoryToMessages(memoryData, this.humanPrefix, this.aiPrefix) : [condenseZepMemoryIntoHumanMessage(memoryData)] };
return { [this.memoryKey]: this.separateMessages ? getBufferString(zepMemoryToMessages(memoryData, this.humanPrefix, this.aiPrefix), this.humanPrefix, this.aiPrefix) : condenseZepMemoryIntoHumanMessage(memoryData).content };
}
/**
* Method that saves the input and output messages to the Zep service.
* @param inputValues Input messages to be saved.
* @param outputValues Output messages to be saved.
* @returns Promise that resolves when the messages have been saved.
*/
async saveContext(inputValues, outputValues) {
const input = getInputValue(inputValues, this.inputKey);
const output = getOutputValue(outputValues, this.outputKey);
const memory = new Memory({ messages: [new Message({
role: this.humanPrefix,
content: `${input}`
}), new Message({
role: this.aiPrefix,
content: `${output}`
})] });
const zepClient = await this.zepClientPromise;
if (!zepClient) throw new Error(this.zepInitFailMsg);
if (this.sessionId) try {
await zepClient.memory.addMemory(this.sessionId, memory);
} catch (error) {
console.error("Error adding memory: ", error);
}
await super.saveContext(inputValues, outputValues);
}
/**
* Method that deletes the chat history from the Zep service.
* @returns Promise that resolves when the chat history has been deleted.
*/
async clear() {
const zepClient = await this.zepClientPromise;
if (!zepClient) throw new Error(this.zepInitFailMsg);
try {
await zepClient.memory.deleteMemory(this.sessionId);
} catch (error) {
console.error("Error deleting session: ", error);
}
await super.clear();
}
};
//#endregion
export { ZepMemory, condenseZepMemoryIntoHumanMessage, zepMemoryContextToSystemPrompt, zepMemoryToMessages, zep_exports };
//# sourceMappingURL=zep.js.map