Skip to content

Commit 7bb7664

Browse files
authored
fix(core): use Observation.permalink in build_context to match the search index (#946)
Closes #929 Signed-off-by: phernandez <paul@basicmemory.com>
1 parent db578cc commit 7bb7664

2 files changed

Lines changed: 47 additions & 3 deletions

File tree

src/basic_memory/services/context_service.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,12 @@ async def build_context(
245245
type="observation",
246246
id=obs.id,
247247
title=f"{obs.category}: {obs.content[:50]}...",
248-
permalink=generate_permalink(
249-
f"{primary_item.permalink}/observations/{obs.category}/{obs.content}"
250-
),
248+
# Observation.permalink is the single definition of the
249+
# synthetic permalink format (200-char truncation plus
250+
# content digest); rebuilding it inline diverged from the
251+
# search index for long observations (#929). The parent
252+
# entity is eager-loaded by ObservationRepository.
253+
permalink=obs.permalink,
251254
file_path=primary_item.file_path,
252255
content=obs.content,
253256
category=obs.category,

tests/services/test_context_service.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,47 @@ async def test_build_context_with_observations(context_service, test_graph):
212212
assert "Root note" in note_observation.content
213213

214214

215+
@pytest.mark.asyncio
216+
async def test_build_context_observation_permalinks_match_search_index(
217+
context_service, search_service, entity_service
218+
):
219+
"""Regression test for #929: observation permalinks must match the search index.
220+
221+
build_context used to rebuild the synthetic observation permalink inline,
222+
without the 200-char truncation (#446) or the content digest (#931) that
223+
Observation.permalink applies, so for long observations it returned
224+
permalinks the search index doesn't contain.
225+
"""
226+
from basic_memory.schemas.base import Entity as EntitySchema
227+
from basic_memory.schemas.search import SearchQuery
228+
229+
long_observation = "x" * 210 + " LONG_OBS_MARKER"
230+
entity, _ = await entity_service.create_or_update_entity(
231+
EntitySchema(
232+
title="Long Obs Entity",
233+
note_type="test",
234+
directory="test",
235+
content=f"# Long Obs Entity\n- [note] {long_observation}\n",
236+
)
237+
)
238+
await search_service.index_entity(entity)
239+
240+
url = memory_url.validate_strings(f"memory://{entity.permalink}")
241+
context_result = await context_service.build_context(url, include_observations=True)
242+
assert len(context_result.results) == 1
243+
context_item = context_result.results[0]
244+
assert len(context_item.observations) == 1
245+
obs_row = context_item.observations[0]
246+
247+
# The model property is the single definition of the permalink format
248+
assert obs_row.permalink == entity.observations[0].permalink
249+
250+
# The search index row for this observation carries the same permalink
251+
index_rows = await search_service.search(SearchQuery(text="LONG_OBS_MARKER"))
252+
obs_permalinks = [r.permalink for r in index_rows if r.type == SearchItemType.OBSERVATION.value]
253+
assert obs_permalinks == [obs_row.permalink]
254+
255+
215256
@pytest.mark.asyncio
216257
async def test_build_context_not_found(context_service):
217258
"""Test handling non-existent permalinks."""

0 commit comments

Comments
 (0)