Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from typing import List, Optional, Sequence | |
| from kbdebugger.compat.langchain import Document | |
| from kbdebugger.types.ui import ProgressCallback | |
| from .keyword_synonyms import generate_synonyms_for_keyword | |
| from .keyBERT import run_keybert_matching | |
| from .types import KeywordDocMatchResult | |
| def filter_paragraphs_by_keyword( | |
| *, | |
| paragraphs: Sequence[Document], | |
| search_keyword: str, | |
| max_synonyms: int = 10, | |
| progress: Optional[ProgressCallback] = None, | |
| ) -> tuple[ | |
| KeywordDocMatchResult, | |
| dict # logging payload | |
| ]: | |
| """ | |
| Public API: Generate synonyms then run KeyBERT keyword extraction + matching to filter paragraphs | |
| by their relevance to the user-chosen keyword. | |
| Parameters | |
| ---------- | |
| paragraphs: | |
| Input documents from Docling (or any upstream). | |
| search_keyword: | |
| The keyword used to find relevant paragraphs. | |
| max_synonyms: | |
| Safety cap for synonym list size (if your generator supports it). | |
| progress: | |
| Callback function to update progress | |
| Notes: | |
| ----- | |
| - KeyBERT operates on strings, but we keep Documents as the canonical objects. | |
| - We do NOT filter out empty texts (if any) here to keep indices stable: | |
| i.e., ParagraphMatch.index == docs[index] | |
| Returns | |
| ------- | |
| KeywordMatchResult | |
| Matched/unmatched paragraphs plus the synonyms that were used. | |
| """ | |
| synonyms = generate_synonyms_for_keyword(search_keyword) | |
| if max_synonyms and len(synonyms) > max_synonyms: | |
| synonyms = synonyms[:max_synonyms] | |
| # Extract paragraph strings from Document objects. | |
| texts = [paragraph_doc.page_content for paragraph_doc in paragraphs] # guaranteed non-empty | |
| matched, unmatched, log_payload = run_keybert_matching( | |
| paragraphs=texts, | |
| search_keyword=search_keyword, | |
| synonyms=synonyms, | |
| progress=progress | |
| ) | |
| # ⚠️ Notice that we ignore the matched/unmatched ParagraphMatch objects here since they contain | |
| # text and keyword info that would be redundant with the Document objects. | |
| # Anyways, they were all logged in a JSON file for inspection/debugging. | |
| # But for downstream stages, we only care about the Document objects that correspond to matched vs unmatched paragraphs. | |
| # We can always add them back later if needed. | |
| # Map back to Document objects for the final result. | |
| matched_docs = [paragraphs[m.index] for m in matched] | |
| unmatched_docs = [paragraphs[u.index] for u in unmatched] | |
| return KeywordDocMatchResult( | |
| matched_docs=matched_docs, | |
| unmatched_docs=unmatched_docs, | |
| synonyms=synonyms, | |
| # matched=matched, | |
| # unmatched=unmatched, | |
| ), log_payload |