"""Context builder for translation prompts.""" from __future__ import annotations from src.context.glossary import GlossaryManager from src.context.pericope import PericopeManager from src.data.loader import DataLoader from src.models.pericope import ChunkTranslationContext, Pericope from src.models.verse import TranslationContext, Verse, VerseReference class ContextBuilder: """Builds translation context for verses and chunks.""" def __init__( self, loader: DataLoader, glossary: GlossaryManager, context_window: int = 3, pericope_manager: PericopeManager | None = None, ) -> None: """Initialize the context builder. Args: loader: Data loader for source texts. glossary: Glossary manager for theological terms. context_window: Number of verses before/after to include. pericope_manager: Optional pericope manager for chunk translation. """ self.loader = loader self.glossary = glossary self.context_window = context_window self.pericope_manager = pericope_manager or PericopeManager() def build_context(self, reference: VerseReference) -> TranslationContext: """Build full translation context for a verse. Args: reference: The verse reference to build context for. Returns: TranslationContext with all available context. """ # Load the target verse target_verse = self.loader.load_verse(reference) # Load surrounding verses preceding = self._get_preceding_verses(reference) following = self._get_following_verses(reference) # Load chapter summary chapter_summary = self.loader.load_chapter_summary( reference.book, reference.chapter ) # Load book metadata book_metadata = self.loader.load_book_metadata(reference.book) # Find relevant glossary terms from the source text glossary_terms = self._find_relevant_terms(target_verse) return TranslationContext( target_verse=target_verse, preceding_verses=preceding, following_verses=following, chapter_summary=chapter_summary, book_metadata=book_metadata, glossary_terms=glossary_terms, ) def _get_preceding_verses(self, reference: VerseReference) -> list[Verse]: """Get verses preceding the target verse.""" verses = [] book = reference.book chapter = reference.chapter verse_num = reference.verse for i in range(self.context_window, 0, -1): target_verse = verse_num - i if target_verse >= 1: ref = VerseReference(book=book, chapter=chapter, verse=target_verse) verse = self.loader.load_verse(ref) # Only include if verse has content if verse.has_source_text() or verse.english_kjv: verses.append(verse) return verses def _get_following_verses(self, reference: VerseReference) -> list[Verse]: """Get verses following the target verse.""" verses = [] book = reference.book chapter = reference.chapter verse_num = reference.verse for i in range(1, self.context_window + 1): target_verse = verse_num + i ref = VerseReference(book=book, chapter=chapter, verse=target_verse) verse = self.loader.load_verse(ref) # Only include if verse has content if verse.has_source_text() or verse.english_kjv: verses.append(verse) return verses def _find_relevant_terms(self, verse: Verse) -> list: """Find glossary terms relevant to a verse.""" terms = [] # Search in Greek text if verse.greek_text: terms.extend(self.glossary.find_terms_in_text(verse.greek_text)) # Search in Greek transliteration if verse.greek_translit: for term in self.glossary.find_terms_in_text(verse.greek_translit): if term not in terms: terms.append(term) # Search in Hebrew text if verse.hebrew_text: for term in self.glossary.find_terms_in_text(verse.hebrew_text): if term not in terms: terms.append(term) return terms def build_chunk_context( self, book: str, pericope: Pericope ) -> ChunkTranslationContext: """Build full translation context for a pericope/chunk. Args: book: Book name. pericope: The pericope to build context for. Returns: ChunkTranslationContext with all available context. """ # Load all verses in the pericope verses = [] for verse_ref in pericope.verses: chapter, verse_num = map(int, verse_ref.split(":")) ref = VerseReference(book=book, chapter=chapter, verse=verse_num) verse = self.loader.load_verse(ref) verses.append(verse) # Load preceding context (verses before first verse) preceding = self._get_chunk_preceding_verses(book, pericope) # Load following context (verses after last verse) following = self._get_chunk_following_verses(book, pericope) # Load chapter summary (use first chapter) chapter_summary = self.loader.load_chapter_summary( book, pericope.start_chapter ) # Load book metadata book_metadata = self.loader.load_book_metadata(book) # Find glossary terms from all verses glossary_terms = self._find_chunk_terms(verses) return ChunkTranslationContext( pericope=pericope, book=book, verses=verses, preceding_verses=preceding, following_verses=following, chapter_summary=chapter_summary, book_metadata=book_metadata, glossary_terms=glossary_terms, ) def build_chunk_context_by_id( self, pericope_id: str ) -> ChunkTranslationContext | None: """Build chunk context by pericope ID. Args: pericope_id: The pericope identifier. Returns: ChunkTranslationContext or None if pericope not found. """ result = self.pericope_manager.get_pericope_by_id(pericope_id) if result is None: return None book, pericope = result return self.build_chunk_context(book, pericope) def build_chunk_context_for_verse( self, reference: VerseReference ) -> ChunkTranslationContext | None: """Build chunk context for the pericope containing a verse. Args: reference: Verse reference. Returns: ChunkTranslationContext or None if no pericope found. """ pericope = self.pericope_manager.get_pericope( reference.book, reference.chapter, reference.verse ) if pericope is None: return None return self.build_chunk_context(reference.book, pericope) def _get_chunk_preceding_verses( self, book: str, pericope: Pericope ) -> list[Verse]: """Get verses preceding the pericope for context.""" verses = [] chapter = pericope.start_chapter verse_num = pericope.start_verse for i in range(self.context_window, 0, -1): target_verse = verse_num - i if target_verse >= 1: ref = VerseReference(book=book, chapter=chapter, verse=target_verse) verse = self.loader.load_verse(ref) if verse.has_source_text() or verse.english_kjv: verses.append(verse) return verses def _get_chunk_following_verses( self, book: str, pericope: Pericope ) -> list[Verse]: """Get verses following the pericope for context.""" verses = [] chapter = pericope.end_chapter verse_num = pericope.end_verse for i in range(1, self.context_window + 1): target_verse = verse_num + i ref = VerseReference(book=book, chapter=chapter, verse=target_verse) verse = self.loader.load_verse(ref) if verse.has_source_text() or verse.english_kjv: verses.append(verse) return verses def _find_chunk_terms(self, verses: list[Verse]) -> list: """Find glossary terms relevant to all verses in a chunk.""" terms = [] seen = set() for verse in verses: verse_terms = self._find_relevant_terms(verse) for term in verse_terms: if term.original not in seen: terms.append(term) seen.add(term.original) return terms