Zep memory (#4898)

Co-authored-by: Daniel Chalef <daniel.chalef@private.org>
Co-authored-by: Daniel Chalef <131175+danielchalef@users.noreply.github.com>
This commit is contained in:
Davis Chase 2023-05-17 20:01:01 -07:00 committed by GitHub
parent e28bdf4453
commit 8966f61ca5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 703 additions and 11 deletions

View File

@ -0,0 +1,439 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Zep Memory\n",
"\n",
"## REACT Agent Chat Message History Example\n",
"\n",
"This notebook demonstrates how to use the [Zep Long-term Memory Store](https://getzep.github.io/) as memory for your chatbot.\n",
"\n",
"We'll demonstrate:\n",
"\n",
"1. Adding conversation history to the Zep memory store.\n",
"2. Running an agent and having message automatically added to the store.\n",
"3. Viewing the enriched messages.\n",
"4. Vector search over the conversation history.\n",
"\n",
"More on Zep:\n",
"\n",
"Zep stores, summarizes, embeds, indexes, and enriches conversational AI chat histories, and exposes them via simple, low-latency APIs.\n",
"\n",
"Key Features:\n",
"\n",
"- Long-term memory persistence, with access to historical messages irrespective of your summarization strategy.\n",
"- Auto-summarization of memory messages based on a configurable message window. A series of summaries are stored, providing flexibility for future summarization strategies.\n",
"- Vector search over memories, with messages automatically embedded on creation.\n",
"- Auto-token counting of memories and summaries, allowing finer-grained control over prompt assembly.\n",
"- Python and JavaScript SDKs.\n",
"\n",
"Zep project: [https://github.com/getzep/zep](https://github.com/getzep/zep)\n",
"Docs: [https://getzep.github.io](https://getzep.github.io/)\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:12:58.500164Z",
"start_time": "2023-05-17T05:12:57.688886Z"
}
},
"outputs": [],
"source": [
"from langchain.memory.chat_message_histories import ZepChatMessageHistory\n",
"from langchain.memory import ConversationBufferMemory\n",
"from langchain import OpenAI\n",
"from langchain.schema import HumanMessage, AIMessage\n",
"from langchain.tools import DuckDuckGoSearchRun\n",
"from langchain.agents import initialize_agent, AgentType\n",
"from uuid import uuid4\n",
"\n",
"\n",
"# Set this to your Zep server URL\n",
"ZEP_API_URL = \"http://localhost:8000\"\n",
"\n",
"session_id = str(uuid4()) # This is a unique identifier for the user\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:12:58.506469Z",
"start_time": "2023-05-17T05:12:58.501324Z"
}
},
"outputs": [],
"source": [
"# Zep is async-first. Our sync APIs use an asyncio wrapper to run outside of an app's event loop.\n",
"# This interferes with Jupyter's event loop, so we need to install nest_asyncio to run the\n",
"# Zep client in a notebook.\n",
"\n",
"# !pip install nest_asyncio\n",
"\n",
"import nest_asyncio\n",
"\n",
"nest_asyncio.apply()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:12:58.514638Z",
"start_time": "2023-05-17T05:12:58.503811Z"
}
},
"outputs": [
{
"data": {
"text/plain": "True"
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Load your OpenAI key from a .env file\n",
"from dotenv import load_dotenv\n",
"\n",
"load_dotenv()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Initialize the Zep Chat Message History Class and initialize the Agent\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:12:58.618633Z",
"start_time": "2023-05-17T05:12:58.510414Z"
}
},
"outputs": [],
"source": [
"ddg = DuckDuckGoSearchRun()\n",
"tools = [ddg]\n",
"\n",
"# Set up Zep Chat History\n",
"zep_chat_history = ZepChatMessageHistory(\n",
" session_id=session_id,\n",
" url=ZEP_API_URL,\n",
")\n",
"\n",
"# Use a standard ConversationBufferMemory to encapsulate the Zep chat history\n",
"memory = ConversationBufferMemory(\n",
" memory_key=\"chat_history\", chat_memory=zep_chat_history\n",
")\n",
"\n",
"# Initialize the agent\n",
"llm = OpenAI(temperature=0)\n",
"agent_chain = initialize_agent(\n",
" tools,\n",
" llm,\n",
" agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,\n",
" verbose=True,\n",
" memory=memory,\n",
")\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Add some history data\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:12:58.764097Z",
"start_time": "2023-05-17T05:12:58.622528Z"
}
},
"outputs": [],
"source": [
"# Preload some messages into the memory. The default message window is 12 messages. We want to push beyond this to demonstrate auto-summarization.\n",
"test_history = [\n",
" {\"role\": \"human\", \"content\": \"Who was Octavia Butler?\"},\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": (\n",
" \"Octavia Estelle Butler (June 22, 1947 February 24, 2006) was an American\"\n",
" \" science fiction author.\"\n",
" ),\n",
" },\n",
" {\"role\": \"human\", \"content\": \"Which books of hers were made into movies?\"},\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": (\n",
" \"The most well-known adaptation of Octavia Butler's work is the FX series\"\n",
" \" Kindred, based on her novel of the same name.\"\n",
" ),\n",
" },\n",
" {\"role\": \"human\", \"content\": \"Who were her contemporaries?\"},\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": (\n",
" \"Octavia Butler's contemporaries included Ursula K. Le Guin, Samuel R.\"\n",
" \" Delany, and Joanna Russ.\"\n",
" ),\n",
" },\n",
" {\"role\": \"human\", \"content\": \"What awards did she win?\"},\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": (\n",
" \"Octavia Butler won the Hugo Award, the Nebula Award, and the MacArthur\"\n",
" \" Fellowship.\"\n",
" ),\n",
" },\n",
" {\n",
" \"role\": \"human\",\n",
" \"content\": \"Which other women sci-fi writers might I want to read?\",\n",
" },\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": \"You might want to read Ursula K. Le Guin or Joanna Russ.\",\n",
" },\n",
" {\n",
" \"role\": \"human\",\n",
" \"content\": (\n",
" \"Write a short synopsis of Butler's book, Parable of the Sower. What is it\"\n",
" \" about?\"\n",
" ),\n",
" },\n",
" {\n",
" \"role\": \"ai\",\n",
" \"content\": (\n",
" \"Parable of the Sower is a science fiction novel by Octavia Butler,\"\n",
" \" published in 1993. It follows the story of Lauren Olamina, a young woman\"\n",
" \" living in a dystopian future where society has collapsed due to\"\n",
" \" environmental disasters, poverty, and violence.\"\n",
" ),\n",
" },\n",
"]\n",
"\n",
"for msg in test_history:\n",
" zep_chat_history.append(\n",
" HumanMessage(content=msg[\"content\"])\n",
" if msg[\"role\"] == \"human\"\n",
" else AIMessage(content=msg[\"content\"])\n",
" )\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Run the agent\n",
"\n",
"Doing so will automatically add the input and response to the Zep memory.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:13:08.226487Z",
"start_time": "2023-05-17T05:12:58.760795Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
"\u001B[32;1m\u001B[1;3mAI: Parable of the Sower is a powerful exploration of the challenges facing contemporary society, such as environmental disasters, poverty, and violence. It examines how these issues can lead to the breakdown of society and how individuals can take action to create a better future. The novel also explores themes of faith, hope, and resilience in the face of adversity.\u001B[0m\n",
"\n",
"\u001B[1m> Finished chain.\u001B[0m\n"
]
},
{
"data": {
"text/plain": "'Parable of the Sower is a powerful exploration of the challenges facing contemporary society, such as environmental disasters, poverty, and violence. It examines how these issues can lead to the breakdown of society and how individuals can take action to create a better future. The novel also explores themes of faith, hope, and resilience in the face of adversity.'"
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_chain.run(\n",
" input=\"WWhat is the book's relevance to the challenges facing contemporary society?\"\n",
")\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Inspect the Zep memory\n",
"\n",
"Note the summary, and that the history has been enriched with token counts, UUIDs, and timestamps.\n",
"\n",
"Summaries are biased towards the most recent messages.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:13:08.240995Z",
"start_time": "2023-05-17T05:13:08.227744Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The AI provides a summary of Octavia Butler's science fiction novel, Parable of the Sower, which follows the story of Lauren Olamina in a dystopian future. When asked for recommendations of other women sci-fi writers, the AI suggests Ursula K. Le Guin and Joanna Russ. The AI also mentions Butler's awards, including the Hugo Award, the Nebula Award, and the MacArthur Fellowship.\n",
"\n",
"\n",
"{'role': 'ai', 'content': 'Parable of the Sower is a powerful exploration of the challenges facing contemporary society, such as environmental disasters, poverty, and violence. It examines how these issues can lead to the breakdown of society and how individuals can take action to create a better future. The novel also explores themes of faith, hope, and resilience in the face of adversity.', 'uuid': '50836d01-9dfe-4dd9-8245-3dd67c4c5c73', 'created_at': '2023-05-17T05:13:08.221879Z', 'token_count': 0}\n",
"{'role': 'human', 'content': \"WWhat is the book's relevance to the challenges facing contemporary society?\", 'uuid': '4249ee05-22df-41b5-a8e3-e2f9386f9ca8', 'created_at': '2023-05-17T05:13:08.211224Z', 'token_count': 0}\n",
"{'role': 'ai', 'content': 'Parable of the Sower is a science fiction novel by Octavia Butler, published in 1993. It follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.', 'uuid': 'd4806cc0-c75e-4c36-80e9-2d74b23aece0', 'created_at': '2023-05-17T05:12:58.757654Z', 'token_count': 56}\n",
"{'role': 'human', 'content': \"Write a short synopsis of Butler's book, Parable of the Sower. What is it about?\", 'uuid': '378f8b63-8ef8-4fe7-b0b2-abc3e73ce1b2', 'created_at': '2023-05-17T05:12:58.754882Z', 'token_count': 23}\n",
"{'role': 'ai', 'content': 'You might want to read Ursula K. Le Guin or Joanna Russ.', 'uuid': 'b9e52976-3417-4d12-868c-34a197e8111d', 'created_at': '2023-05-17T05:12:58.75184Z', 'token_count': 18}\n",
"{'role': 'human', 'content': 'Which other women sci-fi writers might I want to read?', 'uuid': '64c606c8-3c4b-424b-855e-df71825c15d7', 'created_at': '2023-05-17T05:12:58.746194Z', 'token_count': 14}\n",
"{'role': 'ai', 'content': 'Octavia Butler won the Hugo Award, the Nebula Award, and the MacArthur Fellowship.', 'uuid': '39dab3bd-351b-45df-abec-bba056516700', 'created_at': '2023-05-17T05:12:58.743281Z', 'token_count': 21}\n"
]
}
],
"source": [
"def print_messages(messages):\n",
" for m in messages:\n",
" print(m.to_dict())\n",
"\n",
"\n",
"print(zep_chat_history.zep_summary)\n",
"print(\"\\n\")\n",
"print_messages(zep_chat_history.zep_messages)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Vector search over the Zep memory\n",
"\n",
"Zep provides native vector search over historical conversation memory. Embedding happens automatically.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:13:08.382204Z",
"start_time": "2023-05-17T05:13:08.240341Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'uuid': '64c606c8-3c4b-424b-855e-df71825c15d7', 'created_at': '2023-05-17T05:12:58.746194Z', 'role': 'human', 'content': 'Which other women sci-fi writers might I want to read?', 'token_count': 14} 0.9119339814710342\n",
"{'uuid': 'b9e52976-3417-4d12-868c-34a197e8111d', 'created_at': '2023-05-17T05:12:58.75184Z', 'role': 'ai', 'content': 'You might want to read Ursula K. Le Guin or Joanna Russ.', 'token_count': 18} 0.8533797599308901\n",
"{'uuid': 'e33fd150-d7f1-4539-84cd-61422208c991', 'created_at': '2023-05-17T05:12:58.718873Z', 'role': 'ai', 'content': \"Octavia Butler's contemporaries included Ursula K. Le Guin, Samuel R. Delany, and Joanna Russ.\", 'token_count': 27} 0.8523616510364422\n",
"{'uuid': '727327f0-b3a9-4564-bd07-1f6bf9e344f0', 'created_at': '2023-05-17T05:12:58.671189Z', 'role': 'human', 'content': 'Who was Octavia Butler?', 'token_count': 8} 0.8235829604682462\n",
"{'uuid': 'db4ecb8c-b887-4de4-bf50-1e497b242fa7', 'created_at': '2023-05-17T05:12:58.699086Z', 'role': 'ai', 'content': 'Octavia Estelle Butler (June 22, 1947 February 24, 2006) was an American science fiction author.', 'token_count': 31} 0.8205039511484328\n",
"{'uuid': '39dab3bd-351b-45df-abec-bba056516700', 'created_at': '2023-05-17T05:12:58.743281Z', 'role': 'ai', 'content': 'Octavia Butler won the Hugo Award, the Nebula Award, and the MacArthur Fellowship.', 'token_count': 21} 0.8197832181782329\n",
"{'uuid': 'b809efd0-0611-4f22-b0a6-acfd19f99078', 'created_at': '2023-05-17T05:12:58.706624Z', 'role': 'human', 'content': 'Which books of hers were made into movies?', 'token_count': 11} 0.7955720292889512\n",
"{'uuid': 'a77a9b03-2eaa-45d0-8883-3fbf6e069a84', 'created_at': '2023-05-17T05:12:58.714159Z', 'role': 'human', 'content': 'Who were her contemporaries?', 'token_count': 8} 0.7943586007524974\n",
"{'uuid': 'd4806cc0-c75e-4c36-80e9-2d74b23aece0', 'created_at': '2023-05-17T05:12:58.757654Z', 'role': 'ai', 'content': 'Parable of the Sower is a science fiction novel by Octavia Butler, published in 1993. It follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.', 'token_count': 56} 0.7815872510788034\n",
"{'uuid': '7800b21b-00aa-4f60-a30a-e1c645507e66', 'created_at': '2023-05-17T05:12:58.711137Z', 'role': 'ai', 'content': \"The most well-known adaptation of Octavia Butler's work is the FX series Kindred, based on her novel of the same name.\", 'token_count': 29} 0.781267456930058\n"
]
}
],
"source": [
"search_results = zep_chat_history.search(\"who are some famous women sci-fi authors?\")\n",
"for r in search_results:\n",
" print(r.message, r.dist)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2023-05-17T05:13:08.382385Z",
"start_time": "2023-05-17T05:13:08.380270Z"
}
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 8,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2023-05-17T05:13:08.384260Z",
"start_time": "2023-05-17T05:13:08.381802Z"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -11,6 +11,7 @@ from langchain.memory.chat_message_histories.mongodb import MongoDBChatMessageHi
from langchain.memory.chat_message_histories.postgres import PostgresChatMessageHistory
from langchain.memory.chat_message_histories.redis import RedisChatMessageHistory
from langchain.memory.chat_message_histories.sql import SQLChatMessageHistory
from langchain.memory.chat_message_histories.zep import ZepChatMessageHistory
__all__ = [
"DynamoDBChatMessageHistory",
@ -22,4 +23,5 @@ __all__ = [
"FirestoreChatMessageHistory",
"MongoDBChatMessageHistory",
"CassandraChatMessageHistory",
"ZepChatMessageHistory",
]

View File

@ -0,0 +1,156 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, List, Optional
from langchain.schema import (
AIMessage,
BaseChatMessageHistory,
BaseMessage,
HumanMessage,
)
if TYPE_CHECKING:
from zep_python import Memory, Message, NotFoundError, SearchResult
logger = logging.getLogger(__name__)
class ZepChatMessageHistory(BaseChatMessageHistory):
"""A ChatMessageHistory implementation that uses Zep as a backend.
Recommended usage::
# Set up Zep Chat History
zep_chat_history = ZepChatMessageHistory(
session_id=session_id,
url=ZEP_API_URL,
)
# Use a standard ConversationBufferMemory to encapsulate the Zep chat history
memory = ConversationBufferMemory(
memory_key="chat_history", chat_memory=zep_chat_history
)
Zep provides long-term conversation storage for LLM apps. The server stores,
summarizes, embeds, indexes, and enriches conversational AI chat
histories, and exposes them via simple, low-latency APIs.
For server installation instructions and more, see: https://getzep.github.io/
This class is a thin wrapper around the zep-python package. Additional
Zep functionality is exposed via the `zep_summary` and `zep_messages`
properties.
For more information on the zep-python package, see:
https://github.com/getzep/zep-python
"""
def __init__(
self,
session_id: str,
url: str = "http://localhost:8000",
) -> None:
try:
from zep_python import ZepClient
except ImportError:
raise ValueError(
"Could not import zep-python package. "
"Please install it with `pip install zep-python`."
)
self.zep_client = ZepClient(base_url=url)
self.session_id = session_id
@property
def messages(self) -> List[BaseMessage]: # type: ignore
"""Retrieve messages from Zep memory"""
zep_memory: Optional[Memory] = self._get_memory()
if not zep_memory:
return []
messages: List[BaseMessage] = []
# Extract summary, if present, and messages
if zep_memory.summary:
if len(zep_memory.summary.content) > 0:
messages.append(HumanMessage(content=zep_memory.summary.content))
if zep_memory.messages:
msg: Message
for msg in zep_memory.messages:
if msg.role == "ai":
messages.append(AIMessage(content=msg.content))
else:
messages.append(HumanMessage(content=msg.content))
return messages
@property
def zep_messages(self) -> List[Message]:
"""Retrieve summary from Zep memory"""
zep_memory: Optional[Memory] = self._get_memory()
if not zep_memory:
return []
return zep_memory.messages
@property
def zep_summary(self) -> Optional[str]:
"""Retrieve summary from Zep memory"""
zep_memory: Optional[Memory] = self._get_memory()
if not zep_memory or not zep_memory.summary:
return None
return zep_memory.summary.content
def _get_memory(self) -> Optional[Memory]:
"""Retrieve memory from Zep"""
from zep_python import NotFoundError
try:
zep_memory: Memory = self.zep_client.get_memory(self.session_id)
except NotFoundError:
logger.warning(
f"Session {self.session_id} not found in Zep. Returning None"
)
return None
return zep_memory
def add_user_message(self, message: str) -> None:
self.append(HumanMessage(content=message))
def add_ai_message(self, message: str) -> None:
self.append(AIMessage(content=message))
def append(self, message: BaseMessage) -> None:
"""Append the message to the Zep memory history"""
from zep_python import Memory, Message
zep_message: Message
if isinstance(message, HumanMessage):
zep_message = Message(content=message.content, role="human")
else:
zep_message = Message(content=message.content, role="ai")
zep_memory = Memory(messages=[zep_message])
self.zep_client.add_memory(self.session_id, zep_memory)
def search(self, query: str, limit: Optional[int] = None) -> List[SearchResult]:
"""Search Zep memory for messages matching the query"""
from zep_python import SearchPayload
payload: SearchPayload = SearchPayload(text=query)
return self.zep_client.search_memory(self.session_id, payload, limit=limit)
def clear(self) -> None:
"""Clear session memory from Zep. Note that Zep is long-term storage for memory
and this is not advised unless you have specific data retention requirements.
"""
try:
self.zep_client.delete_memory(self.session_id)
except NotFoundError:
logger.warning(
f"Session {self.session_id} not found in Zep. Skipping delete."
)

33
poetry.lock generated
View File

@ -2233,14 +2233,14 @@ uritemplate = ">=3.0.1,<5"
[[package]]
name = "google-auth"
version = "2.18.0"
version = "2.18.1"
description = "Google Authentication Library"
category = "main"
optional = true
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
files = [
{file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"},
{file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"},
{file = "google-auth-2.18.1.tar.gz", hash = "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb"},
{file = "google_auth-2.18.1-py2.py3-none-any.whl", hash = "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37"},
]
[package.dependencies]
@ -2732,14 +2732,14 @@ files = [
[[package]]
name = "httpcore"
version = "0.17.0"
version = "0.17.1"
description = "A minimal low-level HTTP client."
category = "main"
optional = true
python-versions = ">=3.7"
files = [
{file = "httpcore-0.17.0-py3-none-any.whl", hash = "sha256:0fdfea45e94f0c9fd96eab9286077f9ff788dd186635ae61b312693e4d943599"},
{file = "httpcore-0.17.0.tar.gz", hash = "sha256:cc045a3241afbf60ce056202301b4d8b6af08845e3294055eb26b09913ef903c"},
{file = "httpcore-0.17.1-py3-none-any.whl", hash = "sha256:628e768aaeec1f7effdc6408ba1c3cdbd7487c1fc570f7d66844ec4f003e1ca4"},
{file = "httpcore-0.17.1.tar.gz", hash = "sha256:caf508597c525f9b8bfff187e270666309f63115af30f7d68b16143a403c8356"},
]
[package.dependencies]
@ -6608,7 +6608,6 @@ files = [
{file = "pylance-0.4.12-cp38-abi3-macosx_10_15_x86_64.whl", hash = "sha256:2b86fb8dccc03094c0db37bef0d91bda60e8eb0d1eddf245c6971450c8d8a53f"},
{file = "pylance-0.4.12-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bc82914b13204187d673b5f3d45f93219c38a0e9d0542ba251074f639669789"},
{file = "pylance-0.4.12-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a4bcce77f99ecd4cbebbadb01e58d5d8138d40eb56bdcdbc3b20b0475e7a472"},
{file = "pylance-0.4.12-cp38-abi3-win_amd64.whl", hash = "sha256:9616931c5300030adb9626d22515710a127d1e46a46737a7a0f980b52f13627c"},
]
[package.dependencies]
@ -10214,6 +10213,22 @@ files = [
idna = ">=2.0"
multidict = ">=4.0"
[[package]]
name = "zep-python"
version = "0.25"
description = "Zep stores, manages, enriches, indexes, and searches long-term memory for conversational AI applications. This is the Python client for the Zep service."
category = "main"
optional = true
python-versions = ">=3.8,<4.0"
files = [
{file = "zep_python-0.25-py3-none-any.whl", hash = "sha256:d486afc0d621c3211020d89408940bcad4a062bcbb6f5623bf5152fccad1f87e"},
{file = "zep_python-0.25.tar.gz", hash = "sha256:bc2937c0449a13f2abffee24351aa2e574c977773f453812613e81c5bc253916"},
]
[package.dependencies]
httpx = ">=0.24.0,<0.25.0"
pydantic = ">=1.10.7,<2.0.0"
[[package]]
name = "zipp"
version = "3.15.0"
@ -10294,7 +10309,7 @@ all = ["O365", "aleph-alpha-client", "anthropic", "arxiv", "atlassian-python-api
azure = ["azure-core", "azure-cosmos", "azure-identity", "openai"]
cohere = ["cohere"]
embeddings = ["sentence-transformers"]
extended-testing = ["atlassian-python-api", "beautifulsoup4", "beautifulsoup4", "jq", "lxml", "pandas", "pdfminer-six", "pymupdf", "pypdf", "pypdfium2", "telethon", "tqdm"]
extended-testing = ["atlassian-python-api", "beautifulsoup4", "beautifulsoup4", "jq", "lxml", "pandas", "pdfminer-six", "pymupdf", "pypdf", "pypdfium2", "telethon", "tqdm", "zep-python"]
hnswlib = ["docarray", "hnswlib", "protobuf"]
in-memory-store = ["docarray"]
llms = ["anthropic", "cohere", "huggingface_hub", "manifest-ml", "nlpcloud", "openai", "torch", "transformers"]
@ -10304,4 +10319,4 @@ qdrant = ["qdrant-client"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.8.1,<4.0"
content-hash = "978f11abc5819e182271024a5cbdbe15c2238bd24e00b5cf64a63ce7a8366822"
content-hash = "055d65314e800e0731b086471d357e5fb6bbd265ee9fa2bd3762470152cc3b85"

View File

@ -88,7 +88,7 @@ pypdfium2 = {version = "^4.10.0", optional = true}
gql = {version = "^3.4.1", optional = true}
pandas = {version = "^2.0.1", optional = true}
telethon = {version = "^1.28.5", optional = true}
zep-python = {version="^0.25", optional=true}
[tool.poetry.group.docs.dependencies]
autodoc_pydantic = "^1.8.0"
@ -151,7 +151,6 @@ pymongo = "^4.3.3"
cassandra-driver = "^3.27.0"
arxiv = "^1.4"
[tool.poetry.group.lint.dependencies]
ruff = "^0.0.249"
types-toml = "^0.10.8.1"
@ -197,6 +196,7 @@ extended_testing = [
"beautifulsoup4",
"pandas",
"telethon",
"zep-python"
]
[tool.ruff]

View File

@ -0,0 +1,80 @@
from typing import TYPE_CHECKING
import pytest
from pytest_mock import MockerFixture
from langchain.memory.chat_message_histories import ZepChatMessageHistory
from langchain.schema import AIMessage, HumanMessage
if TYPE_CHECKING:
from zep_python import ZepClient
@pytest.fixture
@pytest.mark.requires("zep_python")
def zep_chat(mocker: MockerFixture) -> ZepChatMessageHistory:
mock_zep_client: ZepClient = mocker.patch("zep_python.ZepClient", autospec=True)
zep_chat: ZepChatMessageHistory = ZepChatMessageHistory(
"test_session", "http://localhost:8000"
)
zep_chat.zep_client = mock_zep_client
return zep_chat
@pytest.mark.requires("zep_python")
def test_messages(mocker: MockerFixture, zep_chat: ZepChatMessageHistory) -> None:
from zep_python import Memory, Message, Summary
mock_memory: Memory = Memory(
summary=Summary(
content="summary",
),
messages=[
Message(content="message", role="ai"),
Message(content="message2", role="human"),
],
)
zep_chat.zep_client.get_memory.return_value = mock_memory # type: ignore
result = zep_chat.messages
assert len(result) == 3
assert isinstance(result[0], HumanMessage) # summary
assert isinstance(result[1], AIMessage)
assert isinstance(result[2], HumanMessage)
@pytest.mark.requires("zep_python")
def test_add_user_message(
mocker: MockerFixture, zep_chat: ZepChatMessageHistory
) -> None:
zep_chat.add_user_message("test message")
zep_chat.zep_client.add_memory.assert_called_once() # type: ignore
@pytest.mark.requires("zep_python")
def test_add_ai_message(mocker: MockerFixture, zep_chat: ZepChatMessageHistory) -> None:
zep_chat.add_ai_message("test message")
zep_chat.zep_client.add_memory.assert_called_once() # type: ignore
@pytest.mark.requires("zep_python")
def test_append(mocker: MockerFixture, zep_chat: ZepChatMessageHistory) -> None:
zep_chat.append(AIMessage(content="test message"))
zep_chat.zep_client.add_memory.assert_called_once() # type: ignore
@pytest.mark.requires("zep_python")
def test_search(mocker: MockerFixture, zep_chat: ZepChatMessageHistory) -> None:
zep_chat.search("test query")
zep_chat.zep_client.search_memory.assert_called_once_with( # type: ignore
"test_session", mocker.ANY, limit=None
)
@pytest.mark.requires("zep_python")
def test_clear(mocker: MockerFixture, zep_chat: ZepChatMessageHistory) -> None:
zep_chat.clear()
zep_chat.zep_client.delete_memory.assert_called_once_with( # type: ignore
"test_session"
)