All checks were successful
Enterprise AI Code Review / ai-review (pull_request) Successful in 38s
249 lines
7.5 KiB
Python
249 lines
7.5 KiB
Python
"""Terminal formatting for CLI responses."""
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
try:
|
|
from rich.console import Console
|
|
from rich.markdown import Markdown
|
|
from rich.panel import Panel
|
|
from rich.text import Text
|
|
|
|
RICH_AVAILABLE = True
|
|
except ImportError:
|
|
RICH_AVAILABLE = False
|
|
|
|
|
|
class ResponseFormatter:
|
|
"""Formats API responses for terminal display."""
|
|
|
|
def __init__(
|
|
self,
|
|
show_mood: bool = True,
|
|
show_relationship: bool = False,
|
|
show_facts: bool = False,
|
|
show_timestamps: bool = False,
|
|
use_rich: bool = True,
|
|
):
|
|
"""Initialize formatter.
|
|
|
|
Args:
|
|
show_mood: Show mood information
|
|
show_relationship: Show relationship information
|
|
show_facts: Show extracted facts
|
|
show_timestamps: Show timestamps
|
|
use_rich: Use rich formatting (if available)
|
|
"""
|
|
self.show_mood = show_mood
|
|
self.show_relationship = show_relationship
|
|
self.show_facts = show_facts
|
|
self.show_timestamps = show_timestamps
|
|
self.use_rich = use_rich and RICH_AVAILABLE
|
|
|
|
if self.use_rich:
|
|
self.console = Console()
|
|
|
|
def format_message(self, role: str, content: str, timestamp: str | None = None) -> str:
|
|
"""Format a chat message.
|
|
|
|
Args:
|
|
role: Message role (user/assistant)
|
|
content: Message content
|
|
timestamp: Optional timestamp
|
|
|
|
Returns:
|
|
str: Formatted message
|
|
"""
|
|
if self.use_rich:
|
|
return self._format_message_rich(role, content, timestamp)
|
|
return self._format_message_plain(role, content, timestamp)
|
|
|
|
def _format_message_plain(self, role: str, content: str, timestamp: str | None = None) -> str:
|
|
"""Format message in plain text.
|
|
|
|
Args:
|
|
role: Message role
|
|
content: Message content
|
|
timestamp: Optional timestamp
|
|
|
|
Returns:
|
|
str: Formatted message
|
|
"""
|
|
prefix = "You" if role == "user" else "Bartender"
|
|
lines = [f"{prefix}: {content}"]
|
|
|
|
if timestamp and self.show_timestamps:
|
|
try:
|
|
dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
|
|
time_str = dt.strftime("%H:%M:%S")
|
|
lines.append(f" [{time_str}]")
|
|
except Exception:
|
|
pass
|
|
|
|
return "\n".join(lines)
|
|
|
|
def _format_message_rich(self, role: str, content: str, timestamp: str | None = None) -> None:
|
|
"""Format message using rich.
|
|
|
|
Args:
|
|
role: Message role
|
|
content: Message content
|
|
timestamp: Optional timestamp
|
|
"""
|
|
if role == "user":
|
|
style = "bold cyan"
|
|
prefix = "You"
|
|
else:
|
|
style = "bold green"
|
|
prefix = "Bartender"
|
|
|
|
text = Text()
|
|
text.append(f"{prefix}: ", style=style)
|
|
text.append(content)
|
|
|
|
if timestamp and self.show_timestamps:
|
|
try:
|
|
dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
|
|
time_str = dt.strftime("%H:%M:%S")
|
|
text.append(f"\n [{time_str}]", style="dim")
|
|
except Exception:
|
|
pass
|
|
|
|
self.console.print(text)
|
|
|
|
def format_response(self, response: dict[str, Any]) -> str:
|
|
"""Format a chat response with metadata.
|
|
|
|
Args:
|
|
response: API response
|
|
|
|
Returns:
|
|
str: Formatted response
|
|
"""
|
|
if self.use_rich:
|
|
return self._format_response_rich(response)
|
|
return self._format_response_plain(response)
|
|
|
|
def _format_response_plain(self, response: dict[str, Any]) -> str:
|
|
"""Format response in plain text.
|
|
|
|
Args:
|
|
response: API response
|
|
|
|
Returns:
|
|
str: Formatted response
|
|
"""
|
|
lines = [f"Bartender: {response['response']}"]
|
|
|
|
# Add metadata
|
|
metadata = []
|
|
|
|
if self.show_mood and response.get("mood"):
|
|
mood = response["mood"]
|
|
metadata.append(f"Mood: {mood['label']}")
|
|
|
|
if self.show_relationship and response.get("relationship"):
|
|
rel = response["relationship"]
|
|
metadata.append(f"Relationship: {rel['level']} ({rel['score']})")
|
|
|
|
if self.show_facts and response.get("extracted_facts"):
|
|
facts = response["extracted_facts"]
|
|
if facts:
|
|
metadata.append(f"Facts learned: {len(facts)}")
|
|
|
|
if metadata:
|
|
lines.append(" " + " | ".join(metadata))
|
|
|
|
return "\n".join(lines)
|
|
|
|
def _format_response_rich(self, response: dict[str, Any]) -> None:
|
|
"""Format response using rich.
|
|
|
|
Args:
|
|
response: API response
|
|
"""
|
|
# Main response
|
|
text = Text()
|
|
text.append("Bartender: ", style="bold green")
|
|
text.append(response["response"])
|
|
self.console.print(text)
|
|
|
|
# Metadata panel
|
|
metadata_lines = []
|
|
|
|
if self.show_mood and response.get("mood"):
|
|
mood = response["mood"]
|
|
mood_line = Text()
|
|
mood_line.append("Mood: ", style="dim")
|
|
mood_line.append(mood["label"], style="yellow")
|
|
mood_line.append(
|
|
f" (v:{mood['valence']:.1f}, a:{mood['arousal']:.1f}, i:{mood['intensity']:.1f})",
|
|
style="dim",
|
|
)
|
|
metadata_lines.append(mood_line)
|
|
|
|
if self.show_relationship and response.get("relationship"):
|
|
rel = response["relationship"]
|
|
rel_line = Text()
|
|
rel_line.append("Relationship: ", style="dim")
|
|
rel_line.append(f"{rel['level']} ({rel['score']})", style="cyan")
|
|
rel_line.append(f" | {rel['interactions_count']} interactions", style="dim")
|
|
metadata_lines.append(rel_line)
|
|
|
|
if self.show_facts and response.get("extracted_facts"):
|
|
facts = response["extracted_facts"]
|
|
if facts:
|
|
facts_line = Text()
|
|
facts_line.append("Facts learned: ", style="dim")
|
|
facts_line.append(f"{len(facts)}", style="magenta")
|
|
metadata_lines.append(facts_line)
|
|
|
|
if metadata_lines:
|
|
self.console.print()
|
|
for line in metadata_lines:
|
|
self.console.print(" ", line)
|
|
|
|
def format_history_message(self, message: dict[str, Any]) -> str:
|
|
"""Format a history message.
|
|
|
|
Args:
|
|
message: History message
|
|
|
|
Returns:
|
|
str: Formatted message
|
|
"""
|
|
return self.format_message(message["role"], message["content"], message.get("timestamp"))
|
|
|
|
def print_error(self, message: str) -> None:
|
|
"""Print an error message.
|
|
|
|
Args:
|
|
message: Error message
|
|
"""
|
|
if self.use_rich:
|
|
self.console.print(f"[bold red]Error:[/bold red] {message}")
|
|
else:
|
|
print(f"Error: {message}")
|
|
|
|
def print_info(self, message: str) -> None:
|
|
"""Print an info message.
|
|
|
|
Args:
|
|
message: Info message
|
|
"""
|
|
if self.use_rich:
|
|
self.console.print(f"[dim]{message}[/dim]")
|
|
else:
|
|
print(message)
|
|
|
|
def print_success(self, message: str) -> None:
|
|
"""Print a success message.
|
|
|
|
Args:
|
|
message: Success message
|
|
"""
|
|
if self.use_rich:
|
|
self.console.print(f"[bold green]✓[/bold green] {message}")
|
|
else:
|
|
print(f"✓ {message}")
|