Spaces:
Paused
Paused
| """ | |
| Trace Converter Registry | |
| Centralized registry for trace format converters, following | |
| the same pattern as SchemaRegistry, DisplayRegistry, and ExportRegistry. | |
| Usage: | |
| from potato.trace_converter.registry import converter_registry | |
| # Convert traces | |
| traces = converter_registry.convert("langchain", data) | |
| # Auto-detect format | |
| format_name = converter_registry.detect_format(data) | |
| # List formats | |
| formats = converter_registry.get_supported_formats() | |
| """ | |
| import logging | |
| from typing import Dict, List, Optional, Any | |
| from .base import BaseTraceConverter, CanonicalTrace | |
| logger = logging.getLogger(__name__) | |
| class TraceConverterRegistry: | |
| """ | |
| Centralized registry for trace format converters. | |
| """ | |
| def __init__(self): | |
| self._converters: Dict[str, BaseTraceConverter] = {} | |
| logger.debug("TraceConverterRegistry initialized") | |
| def register(self, converter: BaseTraceConverter) -> None: | |
| """Register a converter instance.""" | |
| name = converter.format_name | |
| if not name: | |
| raise ValueError("Converter must have a non-empty format_name") | |
| if name in self._converters: | |
| raise ValueError(f"Converter '{name}' is already registered") | |
| self._converters[name] = converter | |
| logger.debug(f"Registered trace converter: {name}") | |
| def get(self, name: str) -> Optional[BaseTraceConverter]: | |
| """Get a converter by format name.""" | |
| return self._converters.get(name) | |
| def convert(self, format_name: str, data: Any, | |
| options: Optional[Dict] = None) -> List[CanonicalTrace]: | |
| """ | |
| Convert traces using the named format converter. | |
| Args: | |
| format_name: Converter name (e.g., "langchain", "react") | |
| data: Input data to convert | |
| options: Format-specific options | |
| Returns: | |
| List of CanonicalTrace objects | |
| Raises: | |
| ValueError: If format is not registered | |
| """ | |
| converter = self.get(format_name) | |
| if not converter: | |
| supported = ", ".join(sorted(self._converters.keys())) | |
| raise ValueError( | |
| f"Unknown trace format: '{format_name}'. " | |
| f"Supported formats: {supported}" | |
| ) | |
| return converter.convert(data, options) | |
| def detect_format(self, data: Any) -> Optional[str]: | |
| """ | |
| Auto-detect the format of input data. | |
| Args: | |
| data: Parsed input data | |
| Returns: | |
| Format name if detected, None otherwise | |
| """ | |
| for name, converter in self._converters.items(): | |
| try: | |
| if converter.detect(data): | |
| logger.info(f"Auto-detected trace format: {name}") | |
| return name | |
| except Exception: | |
| continue | |
| return None | |
| def get_supported_formats(self) -> List[str]: | |
| """Get sorted list of supported format names.""" | |
| return sorted(self._converters.keys()) | |
| def list_converters(self) -> List[Dict[str, str]]: | |
| """List all registered converters with metadata.""" | |
| return [ | |
| converter.get_format_info() | |
| for converter in sorted(self._converters.values(), | |
| key=lambda c: c.format_name) | |
| ] | |
| def is_registered(self, name: str) -> bool: | |
| """Check if a format is registered.""" | |
| return name in self._converters | |
| # Global registry instance | |
| converter_registry = TraceConverterRegistry() | |
| def _register_builtin_converters(): | |
| """Register all built-in converters. Called on import. | |
| Registration order matters for auto-detection (first match wins). | |
| More specific formats are registered before generic ones to avoid | |
| false positives: | |
| - ReAct before MultiAgent (both have "steps" but ReAct steps have thought/action/observation) | |
| - SWE-bench before ReAct (instance_id is highly specific) | |
| - OTEL before others (trace_id+span_id is unique) | |
| - MCP before others (MCP methods are distinctive) | |
| - Anthropic before OpenAI (content blocks vs string content) | |
| - MultiAgent last among message-based (checks for sender/receiver/agents) | |
| """ | |
| from .converters.react_converter import ReActConverter | |
| from .converters.langchain_converter import LangChainConverter | |
| from .converters.langfuse_converter import LangfuseConverter | |
| from .converters.atif_converter import ATIFConverter | |
| from .converters.webarena_converter import WebArenaConverter | |
| from .converters.swebench_converter import SWEBenchConverter | |
| from .converters.otel_converter import OTELConverter | |
| from .converters.mcp_converter import MCPConverter | |
| from .converters.anthropic_converter import AnthropicConverter | |
| from .converters.openai_converter import OpenAIConverter | |
| from .converters.multi_agent_converter import MultiAgentConverter | |
| from .converters.web_agent_converter import WebAgentConverter | |
| from .converters.claude_code_converter import ClaudeCodeConverter | |
| from .converters.aider_converter import AiderConverter | |
| from .converters.swe_agent_trajectory_converter import SWEAgentTrajectoryConverter | |
| converters = [ | |
| # Existing converters (specific formats first) | |
| ReActConverter(), | |
| LangChainConverter(), | |
| LangfuseConverter(), | |
| ATIFConverter(), | |
| # Web agent (with coordinates/mouse_path) before plain WebArena | |
| WebAgentConverter(), | |
| WebArenaConverter(), | |
| # New converters (specific-to-generic order) | |
| SWEBenchConverter(), | |
| SWEAgentTrajectoryConverter(), # SWE-Agent trajectories before generic | |
| AiderConverter(), # Aider edit blocks | |
| OTELConverter(), | |
| MCPConverter(), | |
| ClaudeCodeConverter(), # Before Anthropic (more specific: coding tool names) | |
| AnthropicConverter(), | |
| OpenAIConverter(), | |
| MultiAgentConverter(), | |
| ] | |
| for converter in converters: | |
| converter_registry.register(converter) | |
| logger.debug(f"Registered {len(converters)} built-in trace converters") | |
| _register_builtin_converters() | |