mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-01 10:42:23 +01:00
Fix transcript search snippet positioning (#6206)
This commit is contained in:
+23
-4
@@ -67,15 +67,34 @@ def _snippet(content: str, query: str, radius: int = 60) -> str:
|
|||||||
if not query:
|
if not query:
|
||||||
return content[: radius * 2]
|
return content[: radius * 2]
|
||||||
|
|
||||||
idx = content.lower().find(query.lower())
|
match_start = -1
|
||||||
if idx == -1:
|
match_length = 0
|
||||||
|
lowered_content = content.lower()
|
||||||
|
for candidate in _query_terms(query):
|
||||||
|
idx = lowered_content.find(candidate.lower())
|
||||||
|
if idx != -1 and (match_start == -1 or idx < match_start):
|
||||||
|
match_start = idx
|
||||||
|
match_length = len(candidate)
|
||||||
|
|
||||||
|
if match_start == -1:
|
||||||
return content[: radius * 2]
|
return content[: radius * 2]
|
||||||
|
|
||||||
start = max(0, idx - radius)
|
start = max(0, match_start - radius)
|
||||||
end = min(len(content), idx + len(query) + radius)
|
end = min(len(content), match_start + match_length + radius)
|
||||||
return ("..." if start > 0 else "") + content[start:end] + ("..." if end < len(content) else "")
|
return ("..." if start > 0 else "") + content[start:end] + ("..." if end < len(content) else "")
|
||||||
|
|
||||||
|
|
||||||
|
def _query_terms(query: str) -> list[str]:
|
||||||
|
"""Return meaningful quoted phrases and tokens in user-entered order."""
|
||||||
|
terms: list[str] = []
|
||||||
|
for match in re.finditer(r'"([^"]+)"|[\w][\w._-]*', query, flags=re.UNICODE):
|
||||||
|
term = match.group(1) if match.group(1) is not None else match.group(0).strip("._-")
|
||||||
|
term = term.strip()
|
||||||
|
if term:
|
||||||
|
terms.append(term)
|
||||||
|
return terms
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_fts_query(query: str) -> str | None:
|
def _sanitize_fts_query(query: str) -> str | None:
|
||||||
"""Convert free text into a conservative FTS5 MATCH query.
|
"""Convert free text into a conservative FTS5 MATCH query.
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
from core.database import Base
|
from core.database import Base
|
||||||
from core.database import ChatMessage as DbChatMessage
|
from core.database import ChatMessage as DbChatMessage
|
||||||
from core.database import Session as DbSession
|
from core.database import Session as DbSession
|
||||||
from src.session_search import SessionSearchResult, search_session_messages
|
from src.session_search import SessionSearchResult, _snippet, search_session_messages
|
||||||
|
|
||||||
|
|
||||||
def _db(with_fts=True):
|
def _db(with_fts=True):
|
||||||
@@ -61,6 +61,47 @@ def _has_fts(db):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_snippet_locates_quoted_phrase_instead_of_raw_query():
|
||||||
|
content = "A" * 100 + "The session search result is useful." + "Z" * 100
|
||||||
|
|
||||||
|
snippet = _snippet(content, '"session search"', radius=20)
|
||||||
|
|
||||||
|
assert "session search" in snippet
|
||||||
|
assert snippet.startswith("...")
|
||||||
|
assert snippet.endswith("...")
|
||||||
|
|
||||||
|
|
||||||
|
def test_snippet_locates_punctuation_bearing_term_case_insensitively():
|
||||||
|
content = "A" * 100 + "Use FOO_BAR.py for routing." + "Z" * 100
|
||||||
|
|
||||||
|
snippet = _snippet(content, "foo_bar.py!", radius=20)
|
||||||
|
|
||||||
|
assert "FOO_BAR.py" in snippet
|
||||||
|
|
||||||
|
|
||||||
|
def test_snippet_uses_first_meaningful_matching_term():
|
||||||
|
content = "A" * 100 + "second appears here; first appears later." + "Z" * 100
|
||||||
|
|
||||||
|
snippet = _snippet(content, "first second", radius=12)
|
||||||
|
|
||||||
|
assert "second appears" in snippet
|
||||||
|
assert "first appears" not in snippet
|
||||||
|
|
||||||
|
|
||||||
|
def test_snippet_falls_back_to_content_start_when_no_term_matches():
|
||||||
|
content = "Beginning of the transcript, followed by more detail."
|
||||||
|
|
||||||
|
assert _snippet(content, '"missing phrase"', radius=10) == content[:20]
|
||||||
|
|
||||||
|
|
||||||
|
def test_snippet_preserves_boundary_markers_and_truncation():
|
||||||
|
content = "A" * 50 + "target" + "Z" * 50
|
||||||
|
|
||||||
|
snippet = _snippet(content, "target", radius=5)
|
||||||
|
|
||||||
|
assert snippet == "...AAAAAtargetZZZZZ..."
|
||||||
|
|
||||||
|
|
||||||
def test_session_search_uses_fts_and_returns_context():
|
def test_session_search_uses_fts_and_returns_context():
|
||||||
db = _db(with_fts=True)
|
db = _db(with_fts=True)
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user