feat(chat): streaming REPL with rich renderer

- chat/renderer.py: Live streaming markdown, injection warning panel, redaction
- chat/history.py: ConversationHistory with system prompt + memory context injection,
  token budget trimming
- chat/session.py: prompt_toolkit REPL, slash commands (/quit /clear /help /memory list),
  vault key retrieval inline at call time (not stored), injection scan after each response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-17 12:52:08 +02:00
parent 1448bb4650
commit 0fe6332316
3 changed files with 235 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
from rich.console import Console
from rich.live import Live
from rich.markdown import Markdown
from rich.panel import Panel
from rich.text import Text
from pyra.security.injection import redact_api_keys
console = Console()
def render_streaming_response(stream) -> str:
"""Consume a litellm streaming response, render markdown progressively, return full text."""
full_text = ""
with Live(console=console, refresh_per_second=8) as live:
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
full_text += delta
safe_text = redact_api_keys(full_text)
live.update(Markdown(safe_text))
return redact_api_keys(full_text)
def render_injection_warning(warnings) -> None:
labels = ", ".join(w.pattern_label for w in warnings)
console.print(Panel(
f"[yellow]Possible prompt injection detected[/yellow]\n"
f"Pattern(s): [bold]{labels}[/bold]\n\n"
"[dim]The response is shown, but treat it with caution.\n"
"Details logged to ~/.pyra/security.log[/dim]",
border_style="yellow",
title="Security Warning",
))
def render_error(message: str) -> None:
console.print(Panel(f"[red]{message}[/red]", border_style="red"))
def render_info(message: str) -> None:
console.print(f"[dim]{message}[/dim]")
def render_system(message: str) -> None:
console.print(Panel(message, border_style="cyan"))