|
| 1 | +--- |
| 2 | +title: Custom Visualizer |
| 3 | +description: Customize conversation visualization with custom highlighting patterns and display options. |
| 4 | +--- |
| 5 | + |
| 6 | +<Note> |
| 7 | +This example is available on GitHub: [examples/01_standalone_sdk/20_custom_visualizer.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/01_standalone_sdk/20_custom_visualizer.py) |
| 8 | +</Note> |
| 9 | + |
| 10 | +Customize how your agent's conversation is displayed by passing a custom `ConversationVisualizer` instance directly to the `visualize` parameter: |
| 11 | + |
| 12 | +```python icon="python" expandable examples/01_standalone_sdk/20_custom_visualizer.py |
| 13 | +"""Example demonstrating custom visualizer usage. |
| 14 | +
|
| 15 | +This example shows how to pass a custom ConversationVisualizer directly |
| 16 | +to the Conversation, making it easy to customize the visualization without |
| 17 | +the need for callbacks. |
| 18 | +""" |
| 19 | + |
| 20 | +import os |
| 21 | + |
| 22 | +from pydantic import SecretStr |
| 23 | + |
| 24 | +from openhands.sdk import LLM, Agent, Conversation |
| 25 | +from openhands.sdk.conversation.visualizer import ConversationVisualizer |
| 26 | + |
| 27 | + |
| 28 | +def main(): |
| 29 | + # Get API key from environment |
| 30 | + api_key = os.environ.get("LLM_API_KEY") |
| 31 | + if not api_key: |
| 32 | + raise ValueError("LLM_API_KEY environment variable is not set") |
| 33 | + |
| 34 | + # Create LLM and Agent |
| 35 | + llm = LLM(model="gpt-4o-mini", api_key=SecretStr(api_key)) |
| 36 | + agent = Agent(llm=llm, tools=[]) |
| 37 | + |
| 38 | + # Create a custom visualizer with specific highlighting |
| 39 | + custom_visualizer = ConversationVisualizer( |
| 40 | + highlight_regex={ |
| 41 | + r"^Reasoning:": "bold cyan", |
| 42 | + r"^Thought:": "bold green", |
| 43 | + r"^Action:": "bold yellow", |
| 44 | + }, |
| 45 | + skip_user_messages=False, # Show user messages |
| 46 | + ) |
| 47 | + |
| 48 | + # Pass the custom visualizer directly to the conversation |
| 49 | + # This is more intuitive than visualize=False + callbacks=[...] |
| 50 | + conversation = Conversation( |
| 51 | + agent=agent, |
| 52 | + workspace="./workspace", |
| 53 | + visualize=custom_visualizer, # Direct and clear! |
| 54 | + ) |
| 55 | + |
| 56 | + # Send a message and run |
| 57 | + conversation.send_message("What is 2 + 2?") |
| 58 | + conversation.run() |
| 59 | + |
| 60 | + print("\n✅ Example completed!") |
| 61 | + print("The conversation used a custom visualizer with custom highlighting.") |
| 62 | + |
| 63 | + |
| 64 | +if __name__ == "__main__": |
| 65 | + main() |
| 66 | +``` |
| 67 | + |
| 68 | +```bash Running the Example |
| 69 | +export LLM_API_KEY="your-api-key" |
| 70 | +cd agent-sdk |
| 71 | +uv run python examples/01_standalone_sdk/20_custom_visualizer.py |
| 72 | +``` |
| 73 | + |
| 74 | +## Creating a Custom Visualizer |
| 75 | + |
| 76 | +Configure a `ConversationVisualizer` with custom highlighting patterns: |
| 77 | + |
| 78 | +```python |
| 79 | +from openhands.sdk.conversation.visualizer import ConversationVisualizer |
| 80 | + |
| 81 | +custom_visualizer = ConversationVisualizer( |
| 82 | + highlight_regex={ |
| 83 | + r"^Reasoning:": "bold cyan", |
| 84 | + r"^Thought:": "bold green", |
| 85 | + r"^Action:": "bold yellow", |
| 86 | + }, |
| 87 | + skip_user_messages=False, # Show user messages |
| 88 | +) |
| 89 | +``` |
| 90 | + |
| 91 | +### Visualization Options |
| 92 | + |
| 93 | +The `visualize` parameter accepts three types: |
| 94 | + |
| 95 | +- **`True`** (default): Use the default visualizer with standard formatting |
| 96 | +- **`False` or `None`**: Disable visualization entirely |
| 97 | +- **`ConversationVisualizer` instance**: Use your custom visualizer |
| 98 | + |
| 99 | +### Before and After |
| 100 | + |
| 101 | +**Previous approach** (confusing): |
| 102 | +```python |
| 103 | +# Had to set visualize=False and pass callback manually |
| 104 | +conversation = Conversation( |
| 105 | + agent=agent, |
| 106 | + workspace=cwd, |
| 107 | + visualize=False, # Confusing: we DO want visualization! |
| 108 | + callbacks=[custom_visualizer.on_event], |
| 109 | +) |
| 110 | +``` |
| 111 | + |
| 112 | +**New approach** (clear and direct): |
| 113 | +```python |
| 114 | +# Pass the visualizer directly |
| 115 | +conversation = Conversation( |
| 116 | + agent=agent, |
| 117 | + workspace=cwd, |
| 118 | + visualize=custom_visualizer, # Direct and clear! |
| 119 | +) |
| 120 | +``` |
| 121 | + |
| 122 | +## Customization Options |
| 123 | + |
| 124 | +### Highlight Patterns |
| 125 | + |
| 126 | +Use regex patterns to highlight specific content: |
| 127 | + |
| 128 | +```python |
| 129 | +highlight_regex={ |
| 130 | + r"^Reasoning:": "bold cyan", # Lines starting with "Reasoning:" |
| 131 | + r"^Thought:": "bold green", # Lines starting with "Thought:" |
| 132 | + r"^Action:": "bold yellow", # Lines starting with "Action:" |
| 133 | + r"\[ERROR\]": "bold red", # Error markers |
| 134 | + r"\[SUCCESS\]": "bold green", # Success markers |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +### Available Colors and Styles |
| 139 | + |
| 140 | +Colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white` |
| 141 | + |
| 142 | +Styles: `bold`, `dim`, `italic`, `underline` |
| 143 | + |
| 144 | +Combine them: `"bold cyan"`, `"underline red"`, `"bold italic green"` |
| 145 | + |
| 146 | +### Message Filtering |
| 147 | + |
| 148 | +Control which messages are displayed: |
| 149 | + |
| 150 | +```python |
| 151 | +ConversationVisualizer( |
| 152 | + skip_user_messages=False, # Show user messages (default: True) |
| 153 | + skip_system_messages=False, # Show system messages (default: False) |
| 154 | +) |
| 155 | +``` |
| 156 | + |
| 157 | +## Advanced Usage |
| 158 | + |
| 159 | +### Combining with Callbacks |
| 160 | + |
| 161 | +You can still use custom callbacks alongside a custom visualizer: |
| 162 | + |
| 163 | +```python |
| 164 | +def my_callback(event): |
| 165 | + # Custom logging or processing |
| 166 | + print(f"Event received: {event.event_type}") |
| 167 | + |
| 168 | +conversation = Conversation( |
| 169 | + agent=agent, |
| 170 | + workspace=cwd, |
| 171 | + visualize=custom_visualizer, |
| 172 | + callbacks=[my_callback], # Additional callbacks |
| 173 | +) |
| 174 | +``` |
| 175 | + |
| 176 | +### Disabling Visualization |
| 177 | + |
| 178 | +To disable visualization entirely: |
| 179 | + |
| 180 | +```python |
| 181 | +conversation = Conversation( |
| 182 | + agent=agent, |
| 183 | + workspace=cwd, |
| 184 | + visualize=None, # or False |
| 185 | +) |
| 186 | +``` |
| 187 | + |
| 188 | +## Next Steps |
| 189 | + |
| 190 | +- **[Callbacks](/sdk/guides/convo-async)** - Learn about custom event callbacks |
| 191 | +- **[Persistence](/sdk/guides/convo-persistence)** - Save and restore conversation state |
| 192 | +- **[Pause and Resume](/sdk/guides/convo-pause-and-resume)** - Control agent execution flow |
0 commit comments