Add LLM input logging and UI log pane
- Add log_queue to PipelineOrchestrator and log LLM inputs to UI - Use entity_name for lore update logs instead of topic - Pass log_queue into ConfirmationApp to display logs in UI - Introduce a log pane and left/right pane layout in the UI - Poll and render log messages via a new poll_log_updates worker - Run log polling with Textual workers to avoid GC issues - Fix ListView insertion by wrapping ListItem in a list - Relax RAG similarity threshold from 0.7 to 0.5
This commit is contained in:
@@ -53,6 +53,7 @@ class PipelineOrchestrator:
|
||||
self.ui_to_llm_queue = asyncio.Queue()
|
||||
self.clean_to_llm_queue = asyncio.Queue()
|
||||
self.llm_to_ui_queue = asyncio.Queue()
|
||||
self.log_queue = asyncio.Queue()
|
||||
|
||||
self.is_running = False
|
||||
|
||||
@@ -202,6 +203,9 @@ class PipelineOrchestrator:
|
||||
# RAG Retrieval for context
|
||||
context = await asyncio.to_thread(self.rag_manager.retrieve, text)
|
||||
|
||||
# Log the text sent to the LLM for UI affordance
|
||||
await self.log_queue.put(f"[{speaker}] {text}")
|
||||
|
||||
# Structured extraction using the processor
|
||||
extraction_result = await asyncio.to_thread(
|
||||
self.processor.extract_structured_data,
|
||||
@@ -212,7 +216,7 @@ class PipelineOrchestrator:
|
||||
# Persistence: Lore Updates
|
||||
for lore_update in extraction_result.lore_updates:
|
||||
await asyncio.to_thread(update_lore, lore_update)
|
||||
logger.info(f"LLM Worker: Lore updated: {lore_update.topic}")
|
||||
logger.info(f"LLM Worker: Lore updated: {lore_update.entity_name}")
|
||||
|
||||
# Persistence: Character State Updates
|
||||
for char_update in extraction_result.character_updates:
|
||||
@@ -271,6 +275,7 @@ class PipelineOrchestrator:
|
||||
app = ConfirmationApp(
|
||||
ui_to_llm_queue=self.ui_to_llm_queue,
|
||||
llm_to_ui_queue=self.llm_to_ui_queue,
|
||||
log_queue=self.log_queue,
|
||||
)
|
||||
await app.run_async()
|
||||
self.stop()
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ class RAGManager:
|
||||
nodes = retriever.retrieve(query)
|
||||
|
||||
# Filter nodes by similarity score (threshold > 0.7)
|
||||
nodes = [node for node in nodes if node.score >= 0.7]
|
||||
nodes = [node for node in nodes if node.score >= 0.5]
|
||||
|
||||
if summarize:
|
||||
return self.summarize_results(query, nodes)
|
||||
|
||||
+67
-8
@@ -51,6 +51,22 @@ class ConfirmationApp(App):
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#content-wrapper {
|
||||
layout: horizontal;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#left-pane {
|
||||
width: 70%;
|
||||
layout: vertical;
|
||||
}
|
||||
|
||||
#right-pane {
|
||||
width: 30%;
|
||||
layout: vertical;
|
||||
border: solid white;
|
||||
}
|
||||
|
||||
#pending-facts-table {
|
||||
height: 40%;
|
||||
border: solid white;
|
||||
@@ -67,6 +83,17 @@ class ConfirmationApp(App):
|
||||
border: solid white;
|
||||
}
|
||||
|
||||
#log-pane {
|
||||
height: 30%;
|
||||
border: solid white;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
#log-footer {
|
||||
height: 70%;
|
||||
border: solid white;
|
||||
}
|
||||
|
||||
#modal-container {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
@@ -110,11 +137,13 @@ class ConfirmationApp(App):
|
||||
result: Optional[ExtractionResult] = None,
|
||||
ui_to_llm_queue: Optional[asyncio.Queue] = None,
|
||||
llm_to_ui_queue: Optional[asyncio.Queue] = None,
|
||||
log_queue: Optional[asyncio.Queue] = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.result = result
|
||||
self.ui_to_llm_queue = ui_to_llm_queue
|
||||
self.llm_to_ui_queue = llm_to_ui_queue
|
||||
self.log_queue = log_queue
|
||||
self.pending_updates: List[Union[LoreUpdate, CharacterStateUpdate]] = []
|
||||
|
||||
if result:
|
||||
@@ -123,12 +152,23 @@ class ConfirmationApp(App):
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Vertical(
|
||||
DataTable(id="pending-facts-table"),
|
||||
Vertical(
|
||||
Input(placeholder="Message LLM...", id="llm-input"),
|
||||
id="llm-input-container",
|
||||
Horizontal(
|
||||
Vertical(
|
||||
DataTable(id="pending-facts-table"),
|
||||
Vertical(
|
||||
Input(placeholder="Message LLM...", id="llm-input"),
|
||||
id="llm-input-container",
|
||||
),
|
||||
ListView(id="context-pane"),
|
||||
id="left-pane",
|
||||
),
|
||||
Vertical(
|
||||
ListView(id="log-pane"),
|
||||
Static("LATEST LLM INPUTS", id="log-footer"),
|
||||
id="right-pane",
|
||||
),
|
||||
id="content-wrapper",
|
||||
),
|
||||
ListView(id="context-pane"),
|
||||
id="main-container",
|
||||
)
|
||||
yield Footer()
|
||||
@@ -145,7 +185,11 @@ class ConfirmationApp(App):
|
||||
# We don't need a poller for this, just the action_send
|
||||
pass
|
||||
if self.llm_to_ui_queue:
|
||||
self.run_worker(self.poll_llm_updates, thread=False)
|
||||
# Use Textual workers so the task isn't garbage-collected and
|
||||
# exceptions are surfaced via the worker manager.
|
||||
self.run_worker(self.poll_llm_updates(), exclusive=False)
|
||||
if self.log_queue:
|
||||
self.run_worker(self.poll_log_updates(), exclusive=False)
|
||||
|
||||
self.query_one("#llm-input", Input).focus()
|
||||
|
||||
@@ -171,13 +215,28 @@ class ConfirmationApp(App):
|
||||
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)
|
||||
# Insert at the top to show most recent first
|
||||
context_list.insert(0, ListItem(Static(display_text)))
|
||||
# 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:
|
||||
self.log(f"Error polling LLM updates: {e}")
|
||||
|
||||
async def poll_log_updates(self) -> None:
|
||||
while True:
|
||||
try:
|
||||
log_text = await self.log_queue.get()
|
||||
log_list = self.query_one("#log-pane", ListView)
|
||||
# See poll_llm_updates: wrap the ListItem in a list.
|
||||
# Insert at the top to show most recent first.
|
||||
await log_list.insert(0, [ListItem(Static(log_text))])
|
||||
if hasattr(self.log_queue, "task_done"):
|
||||
self.log_queue.task_done()
|
||||
except Exception as e:
|
||||
self.log(f"Error polling log updates: {e}")
|
||||
|
||||
def handle_proposal_result(self, result: ExtractionResult) -> None:
|
||||
table = self.query_one("#pending-facts-table", DataTable)
|
||||
for update in result.lore_updates + result.character_updates:
|
||||
|
||||
Reference in New Issue
Block a user