mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
Fix: Beta testing fixes (#16)
Co-authored-by: phernandez <phernandez@basicmachines.co>
This commit is contained in:
@@ -165,7 +165,7 @@ class ContextService:
|
||||
from_id,
|
||||
to_id,
|
||||
relation_type,
|
||||
content,
|
||||
content_snippet as content,
|
||||
category,
|
||||
entity_id,
|
||||
0 as depth,
|
||||
@@ -189,7 +189,7 @@ class ContextService:
|
||||
r.from_id,
|
||||
r.to_id,
|
||||
r.relation_type,
|
||||
r.content,
|
||||
r.content_snippet as content,
|
||||
r.category,
|
||||
r.entity_id,
|
||||
cg.depth + 1,
|
||||
@@ -218,7 +218,7 @@ class ContextService:
|
||||
e.from_id,
|
||||
e.to_id,
|
||||
e.relation_type,
|
||||
e.content,
|
||||
e.content_snippet as content,
|
||||
e.category,
|
||||
e.entity_id,
|
||||
cg.depth + 1, -- Increment depth for entities
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Service for resolving markdown links to permalinks."""
|
||||
|
||||
from typing import Optional, Tuple, List
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from basic_memory.models import Entity
|
||||
from basic_memory.repository.entity_repository import EntityRepository
|
||||
from basic_memory.repository.search_repository import SearchIndexRow
|
||||
from basic_memory.schemas.search import SearchQuery, SearchItemType
|
||||
from basic_memory.services.search_service import SearchService
|
||||
|
||||
@@ -41,8 +40,9 @@ class LinkResolver:
|
||||
return entity
|
||||
|
||||
# 2. Try exact title match
|
||||
entity = await self.entity_repository.get_by_title(clean_text)
|
||||
if entity:
|
||||
found = await self.entity_repository.get_by_title(clean_text)
|
||||
if found and len(found) == 1:
|
||||
entity = found[0]
|
||||
logger.debug(f"Found title match: {entity.title}")
|
||||
return entity
|
||||
|
||||
@@ -54,7 +54,7 @@ class LinkResolver:
|
||||
|
||||
if results:
|
||||
# Look for best match
|
||||
best_match = self._select_best_match(clean_text, results)
|
||||
best_match = min(results, key=lambda x: x.score) # pyright: ignore
|
||||
logger.debug(
|
||||
f"Selected best match from {len(results)} results: {best_match.permalink}"
|
||||
)
|
||||
@@ -88,43 +88,3 @@ class LinkResolver:
|
||||
alias = alias.strip()
|
||||
|
||||
return text, alias
|
||||
|
||||
def _select_best_match(self, search_text: str, results: List[SearchIndexRow]) -> SearchIndexRow:
|
||||
"""Select best match from search results.
|
||||
|
||||
Uses multiple criteria:
|
||||
1. Word matches in title field
|
||||
2. Word matches in path
|
||||
3. Overall search score
|
||||
"""
|
||||
# Get search terms for matching
|
||||
terms = search_text.lower().split()
|
||||
|
||||
# Score each result
|
||||
scored_results = []
|
||||
for result in results:
|
||||
# Start with base score (lower is better)
|
||||
score = result.score or 0
|
||||
|
||||
if result.permalink:
|
||||
# Parse path components
|
||||
path_parts = result.permalink.lower().split("/")
|
||||
last_part = path_parts[-1] if path_parts else ""
|
||||
else:
|
||||
last_part = "" # pragma: no cover
|
||||
|
||||
# Title word match boosts
|
||||
term_matches = [term for term in terms if term in last_part]
|
||||
if term_matches:
|
||||
score *= 0.5 # Boost for each matching term
|
||||
|
||||
# Exact title match is best
|
||||
if last_part == search_text.lower():
|
||||
score *= 0.2
|
||||
|
||||
scored_results.append((score, result))
|
||||
|
||||
# Sort by score (lowest first) and return best
|
||||
scored_results.sort(key=lambda x: x[0], reverse=True)
|
||||
|
||||
return scored_results[0][1]
|
||||
|
||||
@@ -145,6 +145,7 @@ class SearchService:
|
||||
await self.repository.index_item(
|
||||
SearchIndexRow(
|
||||
id=entity.id,
|
||||
entity_id=entity.id,
|
||||
type=SearchItemType.ENTITY.value,
|
||||
title=entity.title,
|
||||
file_path=entity.file_path,
|
||||
@@ -182,29 +183,33 @@ class SearchService:
|
||||
"entity.permalink should not be None for markdown entities"
|
||||
)
|
||||
|
||||
content_parts = []
|
||||
content_stems = []
|
||||
content_snippet = ""
|
||||
title_variants = self._generate_variants(entity.title)
|
||||
content_parts.extend(title_variants)
|
||||
content_stems.extend(title_variants)
|
||||
|
||||
content = await self.file_service.read_entity_content(entity)
|
||||
if content:
|
||||
content_parts.append(content)
|
||||
content_stems.append(content)
|
||||
content_snippet = f"{content[:250]}"
|
||||
|
||||
content_parts.extend(self._generate_variants(entity.permalink))
|
||||
content_parts.extend(self._generate_variants(entity.file_path))
|
||||
content_stems.extend(self._generate_variants(entity.permalink))
|
||||
content_stems.extend(self._generate_variants(entity.file_path))
|
||||
|
||||
entity_content = "\n".join(p for p in content_parts if p and p.strip())
|
||||
entity_content_stems = "\n".join(p for p in content_stems if p and p.strip())
|
||||
|
||||
assert entity.permalink is not None, (
|
||||
"entity.permalink should not be None for markdown entities"
|
||||
)
|
||||
|
||||
# Index entity
|
||||
await self.repository.index_item(
|
||||
SearchIndexRow(
|
||||
id=entity.id,
|
||||
type=SearchItemType.ENTITY.value,
|
||||
title=entity.title,
|
||||
content=entity_content,
|
||||
content_stems=entity_content_stems,
|
||||
content_snippet=content_snippet,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
entity_id=entity.id,
|
||||
@@ -219,12 +224,16 @@ class SearchService:
|
||||
# Index each observation with permalink
|
||||
for obs in entity.observations:
|
||||
# Index with parent entity's file path since that's where it's defined
|
||||
obs_content_stems = "\n".join(
|
||||
p for p in self._generate_variants(obs.content) if p and p.strip()
|
||||
)
|
||||
await self.repository.index_item(
|
||||
SearchIndexRow(
|
||||
id=obs.id,
|
||||
type=SearchItemType.OBSERVATION.value,
|
||||
title=f"{obs.category}: {obs.content[:50]}...",
|
||||
content=obs.content,
|
||||
title=f"{obs.category}: {obs.content[:100]}...",
|
||||
content_stems=obs_content_stems,
|
||||
content_snippet=obs.content,
|
||||
permalink=obs.permalink,
|
||||
file_path=entity.file_path,
|
||||
category=obs.category,
|
||||
@@ -246,11 +255,15 @@ class SearchService:
|
||||
else f"{rel.from_entity.title}"
|
||||
)
|
||||
|
||||
rel_content_stems = "\n".join(
|
||||
p for p in self._generate_variants(relation_title) if p and p.strip()
|
||||
)
|
||||
await self.repository.index_item(
|
||||
SearchIndexRow(
|
||||
id=rel.id,
|
||||
title=relation_title,
|
||||
permalink=rel.permalink,
|
||||
content_stems=rel_content_stems,
|
||||
file_path=entity.file_path,
|
||||
type=SearchItemType.RELATION.value,
|
||||
entity_id=entity.id,
|
||||
|
||||
Reference in New Issue
Block a user