diff --git a/.env b/.env index ee04edb..8a4cd96 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ # D&D Helpers Configuration OPENAI_API_KEY=no-key-required OPENAI_BASE_URL=https://vllm.tipsy.codes/v1 -LLM_MODEL=Intel/gemma-4-31B-it-int4-AutoRound +LLM_MODEL=google/gemma-4-26b-a4b-it #LLM_BACKEND=ollama #LLM_MODEL=gemma:2b WHISPER_MODEL=base diff --git a/src/pipeline/orchestrator.py b/src/pipeline/orchestrator.py index 0c5c277..a26eb50 100644 --- a/src/pipeline/orchestrator.py +++ b/src/pipeline/orchestrator.py @@ -54,6 +54,7 @@ class PipelineOrchestrator: self.clean_to_llm_queue = asyncio.Queue() self.llm_to_ui_queue = asyncio.Queue() self.log_queue = asyncio.Queue() + self.persistence_queue = asyncio.Queue() self.is_running = False @@ -186,8 +187,11 @@ class PipelineOrchestrator: async def feed_ui(): while self.is_running: try: - text = await self.ui_to_llm_queue.get() - await internal_queue.put(("UI", text)) + item = await self.ui_to_llm_queue.get() + if isinstance(item, (LoreUpdate, CharacterStateUpdate)): + await self.persistence_queue.put(item) + else: + await internal_queue.put(("UI", item)) except Exception as e: logger.error(f"LLM Feeder (UI) error: {e}") @@ -215,20 +219,8 @@ class PipelineOrchestrator: context=context, ) - # Persistence: Lore Updates - for lore_update in extraction_result.lore_updates: - file_path = await asyncio.to_thread(update_lore, lore_update) - await asyncio.to_thread(self.rag_manager.ingest_file, file_path) - logger.info( - f"LLM Worker: Lore updated and ingested into RAG: {lore_update.entity_name}" - ) - - # Persistence: Character State Updates - for char_update in extraction_result.character_updates: - await asyncio.to_thread(update_character_state, char_update) - logger.info( - f"LLM Worker: Character {char_update.character_name} state updated." - ) + # Send the entire result to UI for confirmation + await self.llm_to_ui_queue.put(extraction_result) # UI Notification: Context Updates for context_update in extraction_result.context_updates: @@ -245,29 +237,32 @@ class PipelineOrchestrator: for f in feeders: f.cancel() - def _get_wiki_context(self) -> str: + async def persistence_worker(self): """ - Reads all files in the lore directory and returns them as a 저희 context string. + Worker that handles persistence: Confirmed updates -> Disk & RAG. """ - from src.persistence.lore import DATA_LORE_DIR - - wiki_contents = [] - # Recursively find all .md files in the lore directory - for path in DATA_LORE_DIR.rglob("*.md"): + logger.info("Persistence Worker started.") + while self.is_running: try: - with open(path, "r", encoding="utf-8") as f: - content = f.read() - wiki_contents.append( - f"File: {path.relative_to(DATA_LORE_DIR)}\nContent:\n{content}" + update = await self.persistence_queue.get() + if isinstance(update, LoreUpdate): + file_path = await asyncio.to_thread(update_lore, update) + await asyncio.to_thread(self.rag_manager.ingest_file, file_path) + logger.info( + f"Persistence Worker: Lore updated and ingested into RAG: {update.entity_name}" + ) + elif isinstance(update, CharacterStateUpdate): + await asyncio.to_thread(update_character_state, update) + logger.info( + f"Persistence Worker: Character {update.character_name} state updated." ) - except Exception as e: - logger.error(f"Error reading wiki file {path}: {e}") - return ( - "\n\n".join(wiki_contents) - if wiki_contents - else "No wiki knowledge available." - ) + if hasattr(self.persistence_queue, "task_done"): + self.persistence_queue.task_done() + except Exception as e: + logger.error(f"Persistence Worker error: {e}") + + await asyncio.sleep(0.1) async def tui_worker(self): """ @@ -308,6 +303,7 @@ class PipelineOrchestrator: asyncio.create_task(self.stt_worker()), asyncio.create_task(self.clean_worker()), asyncio.create_task(self.llm_worker()), + asyncio.create_task(self.persistence_worker()), asyncio.create_task(self.tui_worker()), ] diff --git a/src/ui/tui.py b/src/ui/tui.py index bc230e2..4222a6e 100644 --- a/src/ui/tui.py +++ b/src/ui/tui.py @@ -213,12 +213,15 @@ class ConfirmationApp(App): while True: try: update = await self.llm_to_ui_queue.get() - display_text = f"Query: {update.query}\nSource: {update.source}\n\n{update.snippet}" - context_list = self.query_one("#context-pane", ListView) - # ListView.insert takes an *iterable* of ListItems; passing a - # bare ListItem raises TypeError because ListItem is not iterable. - # Insert at the top to show most recent first. - await context_list.insert(0, [ListItem(Static(display_text))]) + if isinstance(update, ExtractionResult): + self.handle_proposal_result(update) + elif isinstance(update, ContextUpdate): + display_text = f"Query: {update.query}\nSource: {update.source}\n\n{update.snippet}" + context_list = self.query_one("#context-pane", ListView) + # ListView.insert takes an *iterable* of ListItems; passing a + # bare ListItem raises TypeError because ListItem is not iterable. + # Insert at the top to show most recent first. + await context_list.insert(0, [ListItem(Static(display_text))]) if hasattr(self.llm_to_ui_queue, "task_done"): self.llm_to_ui_queue.task_done() except Exception as e: @@ -270,10 +273,8 @@ class ConfirmationApp(App): return update = self.pending_updates[row_index] - if isinstance(update, LoreUpdate): - await asyncio.to_thread(update_lore, update) - elif isinstance(update, CharacterStateUpdate): - await asyncio.to_thread(update_character_state, update) + if self.ui_to_llm_queue: + self.ui_to_llm_queue.put_nowait(update) self.remove_update(row_index)