> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Display System

> Advanced display callbacks and custom output formatting for PraisonAI Agents

# Display System

The Display System in PraisonAI Agents provides flexible output formatting and custom display callbacks for rich terminal output, logging, and integration with external systems.

## Overview

The display system allows you to customize how agent outputs, task results, and system messages are displayed. It supports both synchronous and asynchronous callbacks, rich formatting, and integration with various output destinations.

## Core Components

### TaskOutput

The `TaskOutput` class represents the result of a task execution:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import TaskOutput

# TaskOutput contains:
# - raw: String output from the task
# - json_output: Parsed JSON (if applicable)
# - pydantic_output: Pydantic model instance (if applicable)
# - task_id: Unique identifier for the task
# - metadata: Rich metadata about execution
```

### ReflectionOutput

Used for agent self-reflection results:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import ReflectionOutput

# ReflectionOutput contains:
# - reflection: The reflection content
# - satisfactory: Boolean indicating if reflection passed
# - improvement_suggestions: List of suggested improvements
```

## Display Callbacks

### Basic Display Callback

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task

def custom_display(output):
    """Custom display function for task outputs"""
    print(f"🎯 Task: {output.task_id}")
    print(f"📝 Result: {output.raw}")
    print(f"⏱️  Time: {output.metadata.get('execution_time', 'N/A')}")

# Use with agent
agent = Agent(
    role="Assistant",
    goal="Help with tasks",
    display_callback=custom_display
)

# Use with task
task = Task(
    description="Generate report",
    agent=agent,
    callback=custom_display
)
```

### Rich Terminal Display

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table

console = Console()

def rich_display(output):
    """Display output using Rich library"""
    # Create a panel for the output
    panel = Panel(
        output.raw,
        title=f"Task: {output.task_id}",
        border_style="green"
    )
    console.print(panel)
    
    # Display metadata in a table
    if output.metadata:
        table = Table(title="Execution Details")
        table.add_column("Metric", style="cyan")
        table.add_column("Value", style="magenta")
        
        for key, value in output.metadata.items():
            table.add_row(key, str(value))
        
        console.print(table)

# Use with agent
agent = Agent(
    role="Developer",
    goal="Write code",
    display_callback=rich_display
)
```

### Async Display Callbacks

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import asyncio
from datetime import datetime

async def async_display(output):
    """Async display callback for non-blocking operations"""
    # Log to async service
    await log_to_service(output)
    
    # Update dashboard
    await update_dashboard({
        'task_id': output.task_id,
        'completed_at': datetime.now(),
        'result': output.raw,
        'metrics': output.metadata
    })
    
    # Send notifications
    if output.metadata.get('priority') == 'high':
        await send_notification(f"High priority task {output.task_id} completed")

# Use with async task
task = Task(
    description="Async operation",
    agent=agent,
    async_execution=True,
    callback=async_display
)
```

## Global Display Configuration

### Setting Default Display

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import set_default_display

def global_display(output):
    """Global display function for all outputs"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {output.task_id}: {output.raw[:100]}...")

# Set as default for all agents/tasks
set_default_display(global_display)
```

### Display Middleware

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
class DisplayMiddleware:
    """Chain multiple display callbacks"""
    
    def __init__(self, callbacks):
        self.callbacks = callbacks
    
    def __call__(self, output):
        for callback in self.callbacks:
            try:
                callback(output)
            except Exception as e:
                print(f"Display callback error: {e}")

# Create middleware
display_chain = DisplayMiddleware([
    console_display,
    file_logger,
    metrics_collector,
    notification_sender
])

agent = Agent(
    role="Worker",
    goal="Process tasks",
    display_callback=display_chain
)
```

## Specialized Display Handlers

### JSON Output Display

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import json

def json_display(output):
    """Specialized display for JSON outputs"""
    if output.json_output:
        # Pretty print JSON
        formatted = json.dumps(output.json_output, indent=2)
        print(f"JSON Output for {output.task_id}:")
        print(formatted)
    else:
        # Fallback for non-JSON
        print(f"Text Output: {output.raw}")

# Use with JSON task
# from pydantic import BaseModel
# class ConfigSchema(BaseModel):
#     key: str
#     value: str
task = Task(
    description="Generate configuration",
    agent=agent,
    output_json=ConfigSchema,  # Define ConfigSchema as shown above
    callback=json_display
)
```

### Code Output Display

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import TerminalFormatter

def code_display(output):
    """Syntax highlighted code display"""
    # Detect language from metadata or content
    language = output.metadata.get('language', 'python')
    
    try:
        lexer = get_lexer_by_name(language)
        formatter = TerminalFormatter()
        highlighted = highlight(output.raw, lexer, formatter)
        print(highlighted)
    except:
        # Fallback to plain text
        print(output.raw)

# Use with code generation
coder = Agent(
    role="Programmer",
    goal="Write code",
    display_callback=code_display
)
```

### Progress Display

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from tqdm import tqdm
import threading

class ProgressDisplay:
    """Display progress for long-running tasks"""
    
    def __init__(self):
        self.progress_bars = {}
    
    def __call__(self, output):
        task_id = output.task_id
        
        if output.metadata.get('status') == 'started':
            # Create progress bar
            total = output.metadata.get('total_items', 100)
            self.progress_bars[task_id] = tqdm(
                total=total,
                desc=task_id,
                unit='items'
            )
        
        elif output.metadata.get('status') == 'progress':
            # Update progress
            if task_id in self.progress_bars:
                progress = output.metadata.get('completed', 1)
                self.progress_bars[task_id].update(progress)
        
        elif output.metadata.get('status') == 'completed':
            # Close progress bar
            if task_id in self.progress_bars:
                self.progress_bars[task_id].close()
                del self.progress_bars[task_id]
            print(f"✓ {task_id}: {output.raw}")

# Use with batch processing
progress_display = ProgressDisplay()

task = Task(
    description="Process large dataset",
    agent=processor,
    task_type="loop",
    loop_data="large_dataset.csv",
    callback=progress_display
)
```

## Logging Integration

### File Logging

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import logging
from pathlib import Path

class FileLogger:
    """Log outputs to files"""
    
    def __init__(self, log_dir="logs"):
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(exist_ok=True)
        
        # Configure logger
        self.logger = logging.getLogger("task_outputs")
        handler = logging.FileHandler(self.log_dir / "tasks.log")
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.INFO)
    
    def __call__(self, output):
        # Log task completion
        self.logger.info(
            f"Task {output.task_id} completed. "
            f"Execution time: {output.metadata.get('execution_time', 'N/A')}"
        )
        
        # Save full output to separate file
        output_file = self.log_dir / f"{output.task_id}.txt"
        output_file.write_text(output.raw)

# Use file logger
file_logger = FileLogger()

agent = Agent(
    role="Analyst",
    goal="Analyze data",
    display_callback=file_logger
)
```

### Structured Logging

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import structlog

# Configure structured logging
structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer()
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    cache_logger_on_first_use=True,
)

def structured_logger(output):
    """Log with structured data"""
    logger = structlog.get_logger()
    
    logger.info(
        "task_completed",
        task_id=output.task_id,
        execution_time=output.metadata.get('execution_time'),
        agent_role=output.metadata.get('agent_role'),
        tools_used=output.metadata.get('tools_used', []),
        quality_score=output.metadata.get('quality_score'),
        result_preview=output.raw[:200]
    )

# Use structured logging
agent = Agent(
    role="Worker",
    goal="Complete tasks",
    display_callback=structured_logger
)
```

## Error Logging

### Error Display Handler

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import json
from datetime import datetime
from praisonaiagents import error_logger

class ErrorDisplay:
    """Enhanced error display and logging"""
    
    def __init__(self, log_file="errors.log"):
        self.log_file = log_file
    
    def __call__(self, output):
        # Check for errors in metadata
        if output.metadata.get('error'):
            error_info = {
                'task_id': output.task_id,
                'error': output.metadata['error'],
                'traceback': output.metadata.get('traceback', ''),
                'timestamp': datetime.now().isoformat()
            }
            
            # Display error
            print(f"❌ Error in {output.task_id}: {error_info['error']}")
            
            # Log to file
            with open(self.log_file, 'a') as f:
                f.write(json.dumps(error_info) + '\n')
            
            # Send alert for critical errors
            if output.metadata.get('severity') == 'critical':
                self.send_alert(error_info)
    
    def send_alert(self, error_info):
        """Send alerts for critical errors"""
        # Implement alerting logic
        pass

# Use error display
error_display = ErrorDisplay()

# Set as global error logger
error_logger.set_handler(error_display)
```

## Integration Examples

### Slack Integration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import os
from slack_sdk import WebClient

class SlackDisplay:
    """Send outputs to Slack"""
    
    def __init__(self, token, channel):
        self.client = WebClient(token=token)
        self.channel = channel
    
    def __call__(self, output):
        # Format message
        blocks = [
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": f"Task Completed: {output.task_id}"
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": output.raw[:500]
                }
            },
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f"*Agent:* {output.metadata.get('agent_role', 'Unknown')}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": f"*Time:* {output.metadata.get('execution_time', 'N/A')}"
                    }
                ]
            }
        ]
        
        # Send to Slack
        self.client.chat_postMessage(
            channel=self.channel,
            blocks=blocks
        )

# Use Slack display
slack_display = SlackDisplay(
    token=os.getenv("SLACK_TOKEN"),
    channel="#task-updates"
)

agent = Agent(
    role="Reporter",
    goal="Generate reports",
    display_callback=slack_display
)
```

### Dashboard Integration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import aiohttp
from datetime import datetime

class DashboardDisplay:
    """Update real-time dashboard"""
    
    def __init__(self, api_url):
        self.api_url = api_url
    
    async def __call__(self, output):
        """Async update to dashboard"""
        payload = {
            'task_id': output.task_id,
            'status': 'completed',
            'result': output.raw,
            'metadata': output.metadata,
            'timestamp': datetime.now().isoformat()
        }
        
        # Post to dashboard API
        async with aiohttp.ClientSession() as session:
            async with session.post(
                f"{self.api_url}/tasks/update",
                json=payload
            ) as response:
                if response.status != 200:
                    print(f"Dashboard update failed: {response.status}")

# Use dashboard display
dashboard = DashboardDisplay("https://dashboard.example.com/api")

task = Task(
    description="Monitor system",
    agent=monitor,
    async_execution=True,
    callback=dashboard
)
```

## Best Practices

1. **Error Handling**: Always wrap display callbacks in try-except blocks
2. **Performance**: Keep display callbacks lightweight for better performance
3. **Async Usage**: Use async callbacks for I/O operations
4. **Composition**: Combine multiple displays using middleware pattern
5. **Testing**: Test display callbacks separately from agent logic

## See Also

* [Task Module](/api/praisonaiagents/task/task) - Task configuration
* [Callbacks](/features/callbacks) - General callback system
* [Telemetry](/features/telemetry) - Usage tracking
