CLI Display Patterns¶
Advanced patterns for building rich command-line interfaces with AF.
Intent¶
CLI applications need more than simple text output. This guide covers:
- Phase-aware display with Rich panels
- Content-type aware rendering (text, markdown, JSON)
- Multi-phase formatting
- Live updating displays
ContentRenderer Pattern¶
Render agent output with content-type awareness:
import json
from typing import Literal
ContentType = Literal["text", "markdown", "json"]
class ContentRenderer:
"""Content-type aware renderer for agent output."""
def __init__(self, content_type: ContentType = "text"):
self.buffer = ""
self.content_type = content_type
def clear(self):
"""Reset buffer state."""
self.buffer = ""
def set_content(self, content: str):
"""Set full content from AgentResult."""
self.buffer = content
def render(self) -> str:
"""Render based on content type."""
if self.content_type == "json":
try:
parsed = json.loads(self.buffer)
return json.dumps(parsed, indent=2)
except json.JSONDecodeError:
return self.buffer
return self.buffer
Multi-Phase Handler¶
Route events to display with phase context:
import agentic_flow as af
class MultiPhaseHandler:
"""Handler that formats output per phase."""
def __init__(self):
self.current_phase: str | None = None
self.phase_results: dict[str, str] = {}
def __call__(self, event):
# Phase boundaries
if isinstance(event, af.PhaseStarted):
self.current_phase = event.label
print(f"\n[{event.label}]")
return
if isinstance(event, af.PhaseEnded):
print(f"[/{event.label}] ({event.elapsed_ms}ms)")
self.current_phase = None
return
# Agent result — full text delivered once
if isinstance(event, af.AgentResult):
content = str(event.content)
if self.current_phase:
self.phase_results[self.current_phase] = content
print(content)
return
Rich Library Integration¶
Using Rich for beautiful terminal output:
from rich.console import Console, Group
from rich.live import Live
from rich.panel import Panel
from rich.markdown import Markdown
from rich.text import Text
import agentic_flow as af
class RichPhaseDisplay:
"""Rich display with per-phase panels."""
def __init__(self, console: Console):
self.console = console
self.current_phase: str | None = None
self.phase_outputs: list[tuple[str, str]] = []
self.pending_output: str = ""
def handler(self, event):
"""Event handler for Rich display."""
if isinstance(event, af.PhaseStarted):
self.current_phase = event.label
self.pending_output = ""
return
if isinstance(event, af.PhaseEnded):
if self.pending_output:
self.phase_outputs.append((event.label, self.pending_output))
self.console.print(Panel(
Markdown(self.pending_output)
if self.looks_like_markdown(self.pending_output)
else Text(self.pending_output),
title=f"[bold]{event.label}[/bold] ({event.elapsed_ms}ms)",
border_style="cyan",
))
self.current_phase = None
return
if isinstance(event, af.AgentResult):
self.pending_output = str(event.content)
return
@staticmethod
def looks_like_markdown(text: str) -> bool:
"""Simple heuristic for markdown detection."""
markers = ["# ", "## ", "- ", "* ", "```", "**", "["]
return any(marker in text for marker in markers)
Complete Example¶
import agentic_flow as af
from rich.console import Console
# Agents
researcher = af.Agent(
name="researcher",
instructions="Research thoroughly.",
model="gpt-5.2",
model_settings=af.reasoning("medium"),
)
# Rich display
console = Console()
display = RichPhaseDisplay(console)
async def research_flow(message: str) -> str:
async with af.phase("Research", persist=True):
return await researcher(message).stream()
runner = af.Runner(flow=research_flow, handler=display.handler)
if __name__ == "__main__":
result = runner.run_sync("Explain quantum computing")
console.print(f"\n[green]Done![/green] {len(result)} chars")
Next: Testing