Update formatter.py

pull/1041/head
CI-DEV 1 week ago committed by GitHub
parent bff7a18047
commit fd95308491
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,5 +1,6 @@
import threading
import time
import re
from typing import Any, Callable, Dict, List, Optional
from rich.console import Console
@ -13,6 +14,8 @@ from rich.progress import (
from rich.table import Table
from rich.text import Text
from rich.spinner import Spinner
from rich.markdown import Markdown
from rich.syntax import Syntax
# Global lock to ensure only a single Rich Live context is active at any moment.
# Rich's Live render is **not** thread-safe; concurrent Live contexts on the same
@ -28,6 +31,136 @@ dashboard_live = None
spinner = Spinner("dots", style="yellow")
class MarkdownOutputHandler:
"""Custom output handler to render content as markdown with simplified syntax highlighting"""
def __init__(self, console: Console):
self.console = console
def _clean_output(self, output: str) -> str:
"""Clean up the output for better markdown rendering"""
if not output:
return ""
# Remove log prefixes and timestamps
output = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \| INFO.*?\|.*?\|', '', output)
output = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \| DEBUG.*?\|.*?\|', '', output)
output = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \| WARNING.*?\|.*?\|', '', output)
output = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \| ERROR.*?\|.*?\|', '', output)
# Remove spinner characters and progress indicators
output = re.sub(r'[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]', '', output)
output = re.sub(r'⠋ Processing\.\.\.', '', output)
output = re.sub(r'⠙ Processing\.\.\.', '', output)
output = re.sub(r'⠹ Processing\.\.\.', '', output)
output = re.sub(r'⠸ Processing\.\.\.', '', output)
output = re.sub(r'⠼ Processing\.\.\.', '', output)
output = re.sub(r'⠴ Processing\.\.\.', '', output)
output = re.sub(r'⠦ Processing\.\.\.', '', output)
output = re.sub(r'⠧ Processing\.\.\.', '', output)
output = re.sub(r'⠇ Processing\.\.\.', '', output)
output = re.sub(r'⠏ Processing\.\.\.', '', output)
# Remove loop indicators
output = re.sub(r'⠋ Loop \d+/\d+', '', output)
output = re.sub(r'⠙ Loop \d+/\d+', '', output)
output = re.sub(r'⠹ Loop \d+/\d+', '', output)
output = re.sub(r'⠸ Loop \d+/\d+', '', output)
output = re.sub(r'⠼ Loop \d+/\d+', '', output)
output = re.sub(r'⠴ Loop \d+/\d+', '', output)
output = re.sub(r'⠦ Loop \d+/\d+', '', output)
output = re.sub(r'⠧ Loop \d+/\d+', '', output)
output = re.sub(r'⠇ Loop \d+/\d+', '', output)
output = re.sub(r'⠏ Loop \d+/\d+', '', output)
# Remove any remaining log messages
output = re.sub(r'INFO.*?\|.*?\|.*?\|', '', output)
output = re.sub(r'DEBUG.*?\|.*?\|.*?\|', '', output)
output = re.sub(r'WARNING.*?\|.*?\|.*?\|', '', output)
output = re.sub(r'ERROR.*?\|.*?\|.*?\|', '', output)
# Clean up extra whitespace and empty lines
output = re.sub(r'\n\s*\n\s*\n', '\n\n', output)
output = re.sub(r'^\s+', '', output, flags=re.MULTILINE)
output = re.sub(r'\s+$', '', output, flags=re.MULTILINE)
# Remove any remaining plaintext artifacts
output = re.sub(r'Generated content:', '', output)
output = re.sub(r'Evaluation result:', '', output)
output = re.sub(r'Refined content:', '', output)
# Ensure proper markdown formatting
if not output.strip().startswith('#'):
# If no headers, add some structure
lines = output.strip().split('\n')
if len(lines) > 0:
# Add a header for the first meaningful line
first_line = lines[0].strip()
if first_line and not first_line.startswith('**'):
output = f"## {first_line}\n\n" + '\n'.join(lines[1:])
return output.strip()
def render_with_simple_syntax_highlighting(self, content: str) -> list:
"""Render content with simplified syntax highlighting for code blocks"""
# For now, let's just render everything as markdown to ensure it works
# We can add code block detection back later if needed
return [('markdown', content)]
def render_content_parts(self, parts: list) -> list:
"""Render different content parts with appropriate formatting"""
rendered_parts = []
for part in parts:
if part[0] == 'markdown':
# Render markdown
try:
md = Markdown(part[1])
rendered_parts.append(md)
except Exception:
# Fallback to plain text
rendered_parts.append(Text(part[1]))
elif part[0] == 'code':
# Code is already rendered as Syntax or Text object
rendered_parts.append(part[1])
return rendered_parts
def render_markdown_output(self, content: str, title: str = "", border_style: str = "blue"):
"""Render content as markdown with syntax highlighting"""
if not content or content.strip() == "":
return
# Clean up the output
cleaned_content = self._clean_output(content)
# Render with syntax highlighting
try:
# Split content into parts (markdown and code blocks)
parts = self.render_with_simple_syntax_highlighting(cleaned_content)
# Render each part appropriately
rendered_parts = self.render_content_parts(parts)
# Create a group of rendered parts
from rich.console import Group
content_group = Group(*rendered_parts)
self.console.print(Panel(
content_group,
title=title,
border_style=border_style
))
except Exception as e:
# Fallback to plain text if rendering fails
self.console.print(Panel(
cleaned_content,
title=title,
border_style="yellow"
))
def choose_random_color():
import random
@ -50,9 +183,12 @@ class Formatter:
A class for formatting and printing rich text to the console.
"""
def __init__(self):
def __init__(self, markdown: bool = True):
"""
Initializes the Formatter with a Rich Console instance.
Args:
markdown (bool): Whether to enable markdown output rendering. Defaults to True.
"""
self.console = Console()
self._dashboard_live = None
@ -69,6 +205,29 @@ class Formatter:
"",
]
self._spinner_idx = 0
self.markdown = markdown
# Initialize markdown output handler if enabled
if self.markdown:
self.markdown_handler = MarkdownOutputHandler(self.console)
else:
self.markdown_handler = None
def enable_markdown(self):
"""Enable markdown output rendering"""
if not self.markdown:
self.markdown = True
self.markdown_handler = MarkdownOutputHandler(self.console)
def disable_markdown(self):
"""Disable markdown output rendering"""
if self.markdown:
self.markdown = False
self.markdown_handler = None
def is_markdown_enabled(self) -> bool:
"""Check if markdown output is enabled"""
return self.markdown
def _get_status_with_loading(self, status: str) -> Text:
"""
@ -142,12 +301,36 @@ class Formatter:
if not isinstance(content, str):
content = str(content)
try:
self._print_panel(content, title, style)
except Exception:
# Fallback to basic printing if panel fails
print(f"\n{title}:")
print(content)
# Use markdown rendering if enabled
if self.markdown and self.markdown_handler:
self.markdown_handler.render_markdown_output(content, title, style)
else:
# Fallback to original panel printing
try:
self._print_panel(content, title, style)
except Exception:
# Fallback to basic printing if panel fails
print(f"\n{title}:")
print(content)
def print_markdown(
self,
content: str,
title: str = "",
border_style: str = "blue",
) -> None:
"""Print content as markdown with syntax highlighting.
Args:
content (str): The content to display as markdown
title (str): The title of the panel
border_style (str): The border style for the panel
"""
if self.markdown_handler:
self.markdown_handler.render_markdown_output(content, title, border_style)
else:
# Fallback to regular panel if markdown is disabled
self.print_panel(content, title, border_style)
def print_table(
self, title: str, data: Dict[str, List[str]]
@ -397,7 +580,7 @@ class Formatter:
def print_agent_dashboard(
self,
agents_data: List[Dict[str, Any]],
title: str = "ConcurrentWorkflow Dashboard",
title: str = "Concurrent Workflow Dashboard",
is_final: bool = False,
) -> None:
"""
@ -440,4 +623,5 @@ class Formatter:
self._dashboard_live = None
formatter = Formatter()
# Global formatter instance with markdown output enabled by default
formatter = Formatter(markdown=True)

Loading…
Cancel
Save