Skip to content

Commit

Permalink
feat(memory): LangChain 'Facts' Support. Refactor Facts (#123)
Browse files Browse the repository at this point in the history
* move facts to sessions

* facts in memory. update langcahin

* deps
  • Loading branch information
danielchalef authored Mar 3, 2024
1 parent c025480 commit e030f52
Show file tree
Hide file tree
Showing 7 changed files with 425 additions and 58 deletions.
415 changes: 394 additions & 21 deletions examples/langchain-langserve/poetry.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions examples/langchain-langserve/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ python = "^3.11"
uvicorn = "^0.23.2"
langserve = {extras = ["server"], version = ">=0.0.30"}
pydantic = ">2, <3"
python-dotenv = "^1.0.1"
zep-python = {path = "../.."}
bs4 = "^0.0.2"
langchain-openai = "^0.0.8"


[tool.poetry.group.dev.dependencies]
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "zep-python"
version = "2.0.0-rc.1"
version = "2.0.0-rc.2"
description = "Zep: Fast, scalable building blocks for LLM apps. This is the Python client for the Zep service."
authors = ["Daniel Chalef <[email protected]>"]
readme = "README.md"
Expand All @@ -9,8 +9,6 @@ readme = "README.md"
python = ">=3.9.0,<4"
httpx = "^0.26.0"
pydantic = ">=2.0.0"
packaging = "^23.1"
black = "24.2.0"

[tool.poetry.group.test.dependencies]
pytest = "^7.3.1"
Expand All @@ -21,6 +19,7 @@ mypy = "^1.2.0"
pytest-mock = "^3.10.0"

[tool.poetry.group.dev.dependencies]
packaging = "^23.1"
black = "^24.2.0"
ruff = ">=0.0.291"
mypy = "^1.2.0"
Expand All @@ -32,7 +31,7 @@ pdoc3 = "^0.10.0"
langchain = "^0.1.3"
openai = "^1.9.0"
langchain-openai = "^0.0.3"
langchain-cli = {extras = ["serve"], version = "^0.0.21"}
langchain-cli = { extras = ["serve"], version = "^0.0.21" }
bs4 = "^0.0.2"

[build-system]
Expand Down
34 changes: 8 additions & 26 deletions tests/memory_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@
]
)

mock_messages_with_facts = mock_messages.copy()
mock_messages_with_facts["facts"] = [
"fact1",
"fact2",
]


def filter_unset_fields(d: Dict) -> Dict:
filtered = {}
Expand All @@ -71,18 +65,16 @@ def filter_unset_fields(d: Dict) -> Dict:
return filtered


def validate_memory(memory: Memory, with_facts: bool = False):
mock_values = mock_messages_with_facts if with_facts else mock_messages

assert len(memory.messages) == len(mock_values["messages"])
def validate_memory(memory: Memory):
assert len(memory.messages) == len(mock_messages["messages"])

for i in range(len(memory.messages)):
assert memory.messages[i].uuid == mock_values["messages"][i]["uuid"]
assert memory.messages[i].role == mock_values["messages"][i]["role"]
assert memory.messages[i].content == mock_values["messages"][i]["content"]
assert memory.messages[i].metadata == mock_values["messages"][i]["metadata"]
assert memory.messages[i].uuid == mock_messages["messages"][i]["uuid"]
assert memory.messages[i].role == mock_messages["messages"][i]["role"]
assert memory.messages[i].content == mock_messages["messages"][i]["content"]
assert memory.messages[i].metadata == mock_messages["messages"][i]["metadata"]

assert filter_unset_fields(memory.model_dump()) == mock_values
assert filter_unset_fields(memory.model_dump()) == mock_messages


@pytest.mark.asyncio
Expand Down Expand Up @@ -130,17 +122,6 @@ async def test_aget_memory_missing_values(httpx_mock: HTTPXMock):
assert len(memory.messages) == 2


@pytest.mark.asyncio
async def test_aget_memory_with_facts(httpx_mock: HTTPXMock):
session_id = str(uuid4())

async with ZepClient(**mock_auth) as client:
httpx_mock.add_response(status_code=200, json=mock_messages_with_facts)
memory = await client.memory.aget_memory(session_id)

validate_memory(memory, with_facts=True)


def test_get_memory(httpx_mock: HTTPXMock):
session_id = str(uuid4())

Expand Down Expand Up @@ -752,6 +733,7 @@ def test_search_memory_invalid_search_type(httpx_mock: HTTPXMock):
"session_id": "abc123",
"metadata": {},
"user_id": "user123",
"facts": ["fact1", "fact2"],
}


Expand Down
10 changes: 7 additions & 3 deletions zep_python/langchain/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class ZepChatMessageHistory(BaseChatMessageHistory):
api_key : str
The Zep API key. Not required if using Zep Open Source.
memory_type : str
The type of memory to use. Can be "message_window" or "perpetual".
The type of memory to use. Can be "perpetual", "summary_retrieval",
or "message_window". Defaults to "perpetual".
"""

def __init__(
Expand All @@ -56,7 +57,7 @@ def __init__(
self._client = zep_client

self.session_id = session_id
self.memory_type = memory_type or "message_window"
self.memory_type = memory_type or "perpetual"

@property
def messages(self) -> List[BaseMessage]: # type: ignore
Expand All @@ -67,7 +68,10 @@ def messages(self) -> List[BaseMessage]: # type: ignore
return []

messages: List[BaseMessage] = []
# Extract summary, if present, and messages
# Extract facts and summary, if present, and messages
if zep_memory.facts:
messages.append(SystemMessage(content="\n".join(zep_memory.facts)))

if zep_memory.summary:
if len(zep_memory.summary.content) > 0:
messages.append(SystemMessage(content=zep_memory.summary.content))
Expand Down
11 changes: 8 additions & 3 deletions zep_python/memory/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class SearchScope(str, Enum):
class MemoryType(str, Enum):
message_window = "message_window"
perpetual = "perpetual"
summary_retriever = "summary_retriever"


class Session(BaseModel):
Expand All @@ -41,6 +42,8 @@ class Session(BaseModel):
The unique identifier of the session.
metadata : Dict[str, Any]
The metadata associated with the session.
facts : Optional[List[str]]
A list of facts derived from the session.
"""

uuid: Optional[str] = None
Expand All @@ -51,6 +54,7 @@ class Session(BaseModel):
session_id: str
user_id: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
facts: Optional[List[str]] = Field(default=None)


class Summary(BaseModel):
Expand Down Expand Up @@ -106,14 +110,15 @@ class Memory(BaseModel):
A dictionary containing metadata associated with the memory.
summary : Optional[Summary]
A Summary object.
facts : Optional[List[str]]
A list of facts derived from the memory.
uuid : Optional[str]
A unique identifier for the memory.
created_at : Optional[str]
The timestamp when the memory was created.
token_count : Optional[int]
The token count of the memory.
facts : Optional[List[str]]
Most recent list of facts derived from the session. Included only with
perpetual memory type.
Methods
-------
Expand All @@ -127,10 +132,10 @@ class Memory(BaseModel):
)
metadata: Optional[Dict[str, Any]] = Field(default=None)
summary: Optional[Summary] = Field(default=None)
facts: Optional[List[str]] = Field(default=None)
uuid: Optional[str] = Field(default=None)
created_at: Optional[str] = Field(default=None)
token_count: Optional[int] = Field(default=None)
facts: Optional[List[str]] = Field(default=None)

def to_dict(self) -> Dict[str, Any]:
return self.model_dump(exclude_unset=True, exclude_none=True)
Expand Down

0 comments on commit e030f52

Please sign in to comment.