Skip to content

Commit e7301a2

Browse files
authored
Add count()support to message history (#517)
Changes for #495 - Add count method to `BaseMessageHistory`, `MessageHistory`, `SemanticMessageHistory` - Add supporting tests to `test_message_history.py` <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds a new abstract `count()` method to `BaseMessageHistory`, which is a small interface change that can break any downstream custom history implementations; runtime behavior depends on Redis query counting semantics. > > **Overview** > Adds a `count(session_tag=None)` API to both `MessageHistory` and `SemanticMessageHistory`, implemented via `CountQuery` to return the number of stored messages (optionally scoped to a provided `session_tag`). > > Updates integration tests to assert counts before/after `clear()`, and extends the message history user guide notebook with a new section demonstrating `.count()` usage. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 119f11d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent e414452 commit e7301a2

File tree

5 files changed

+76
-5
lines changed

5 files changed

+76
-5
lines changed

docs/user_guide/07_message_history.ipynb

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
{
2929
"cell_type": "code",
30-
"execution_count": 1,
30+
"execution_count": 33,
3131
"metadata": {
3232
"execution": {
3333
"iopub.execute_input": "2026-02-16T15:14:26.666540Z",
@@ -55,7 +55,7 @@
5555
},
5656
{
5757
"cell_type": "code",
58-
"execution_count": 2,
58+
"execution_count": 34,
5959
"metadata": {
6060
"execution": {
6161
"iopub.execute_input": "2026-02-16T15:14:26.853637Z",
@@ -172,7 +172,7 @@
172172
},
173173
{
174174
"cell_type": "code",
175-
"execution_count": 5,
175+
"execution_count": 35,
176176
"metadata": {
177177
"execution": {
178178
"iopub.execute_input": "2026-02-16T15:14:26.881966Z",
@@ -370,6 +370,33 @@
370370
" print(message)"
371371
]
372372
},
373+
{
374+
"cell_type": "markdown",
375+
"metadata": {},
376+
"source": [
377+
"## Retrieving message counts\n",
378+
"\n",
379+
"To get the total number of messages stored in a session, use the `.count()` method. \n",
380+
"You can optionally pass a `session_tag` argument to retrieve the count for a different conversation session."
381+
]
382+
},
383+
{
384+
"cell_type": "code",
385+
"execution_count": 38,
386+
"metadata": {},
387+
"outputs": [
388+
{
389+
"name": "stdout",
390+
"output_type": "stream",
391+
"text": [
392+
"Total messages in the session: 7\n"
393+
]
394+
}
395+
],
396+
"source": [
397+
"print(f\"Total messages in the session: {chat_history.count()}\")"
398+
]
399+
},
373400
{
374401
"cell_type": "markdown",
375402
"metadata": {},

redisvl/extensions/message_history/base_history.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ def drop(self, id_field: Optional[str] = None) -> None:
4949
"""
5050
raise NotImplementedError
5151

52+
def count(self, session_tag: Optional[str] = None) -> int:
53+
"""Count the number of messages in the conversation history.
54+
55+
Args:
56+
session_tag (Optional[str]): The session tag to filter messages by.
57+
If None, counts all messages in the history.
58+
"""
59+
raise NotImplementedError
60+
5261
@property
5362
def messages(self) -> Union[List[str], List[Dict[str, str]]]:
5463
"""Returns the full chat history."""

redisvl/extensions/message_history/message_history.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from redisvl.extensions.message_history import BaseMessageHistory
1515
from redisvl.extensions.message_history.schema import ChatMessage, MessageHistorySchema
1616
from redisvl.index import SearchIndex
17-
from redisvl.query import FilterQuery
17+
from redisvl.query import CountQuery, FilterQuery
1818
from redisvl.query.filter import Tag
1919
from redisvl.utils.utils import serialize
2020

@@ -88,6 +88,16 @@ def drop(self, id: Optional[str] = None) -> None:
8888

8989
self._index.client.delete(self._index.key(id)) # type: ignore
9090

91+
def count(self, session_tag=None):
92+
query = CountQuery(
93+
filter_expression=(
94+
Tag(SESSION_FIELD_NAME) == session_tag
95+
if session_tag
96+
else self._default_session_filter
97+
)
98+
)
99+
return self._index.query(query)
100+
91101
@property
92102
def messages(self) -> Union[List[str], List[Dict[str, str]]]:
93103
"""Returns the full message history."""

redisvl/extensions/message_history/semantic_history.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
SemanticMessageHistorySchema,
1919
)
2020
from redisvl.index import SearchIndex
21-
from redisvl.query import FilterQuery, RangeQuery
21+
from redisvl.query import CountQuery, FilterQuery, RangeQuery
2222
from redisvl.query.filter import Tag
2323
from redisvl.utils.utils import deprecated_argument, serialize, validate_vector_dims
2424
from redisvl.utils.vectorize import BaseVectorizer, HFTextVectorizer
@@ -140,6 +140,16 @@ def drop(self, id: Optional[str] = None) -> None:
140140

141141
self._index.client.delete(self._index.key(id)) # type: ignore
142142

143+
def count(self, session_tag=None):
144+
query = CountQuery(
145+
filter_expression=(
146+
Tag(SESSION_FIELD_NAME) == session_tag
147+
if session_tag
148+
else self._default_session_filter
149+
)
150+
)
151+
return self._index.query(query)
152+
143153
@property
144154
def messages(self) -> Union[List[str], List[Dict[str, str]]]:
145155
"""Returns the full message history."""

tests/integration/test_message_history.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ def test_standard_clear(standard_history):
330330
assert empty_context == []
331331

332332

333+
def test_standard_count(standard_history):
334+
standard_history.store("some prompt", "some response")
335+
assert standard_history.count() == 2
336+
standard_history.clear()
337+
assert standard_history.count() == 0
338+
339+
333340
# test semantic message history
334341
@requires_hf
335342
def test_semantic_specify_client(client, hf_vectorizer):
@@ -631,6 +638,14 @@ def test_semantic_drop(semantic_history):
631638
]
632639

633640

641+
@requires_hf
642+
def test_semantic_count(semantic_history):
643+
semantic_history.store("first prompt", "first response")
644+
assert semantic_history.count() == 2
645+
semantic_history.clear()
646+
assert semantic_history.count() == 0
647+
648+
634649
def test_different_vector_dtypes(client, redis_url):
635650
skip_if_no_redisearch(client)
636651
try:

0 commit comments

Comments
 (0)