"""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}")