codebook / potato /trace_converter /converters /react_converter.py
davidjurgens's picture
Deploy: Potato — Codebook Annotation
aceb1b2 verified
Raw
History Blame Contribute Delete
3.39 kB
"""
ReAct JSON Converter
Converts generic ReAct-format traces (thought/action/observation steps)
to Potato's canonical format.
Expected input format:
{
"id": "trace_001",
"task": "Book a flight...",
"steps": [
{"thought": "I need to...", "action": "search(...)", "observation": "Found..."},
...
],
"metadata": {"agent": "GPT-4", "tokens": 2340}
}
"""
from typing import Any, Dict, List, Optional
from ..base import BaseTraceConverter, CanonicalTrace
class ReActConverter(BaseTraceConverter):
"""Converter for generic ReAct JSON traces."""
format_name = "react"
description = "Generic ReAct JSON format (thought/action/observation steps)"
file_extensions = [".json", ".jsonl"]
def convert(self, data: Any, options: Optional[Dict] = None) -> List[CanonicalTrace]:
options = options or {}
traces = data if isinstance(data, list) else [data]
results = []
for item in traces:
trace_id = item.get("id", f"trace_{len(results)}")
task = item.get("task", item.get("task_description", ""))
agent_name = item.get("agent", item.get("agent_name", ""))
steps = item.get("steps", [])
metadata = item.get("metadata", {})
# Build conversation turns
conversation = []
for step in steps:
if "thought" in step and step["thought"]:
conversation.append({
"speaker": "Agent (Thought)",
"text": step["thought"]
})
if "action" in step and step["action"]:
conversation.append({
"speaker": "Agent (Action)",
"text": step["action"]
})
if "observation" in step and step["observation"]:
conversation.append({
"speaker": "Environment",
"text": step["observation"]
})
# Build metadata table
metadata_table = []
metadata_table.append({"Property": "Steps", "Value": str(len(steps))})
for key, value in metadata.items():
metadata_table.append({"Property": key, "Value": str(value)})
trace = CanonicalTrace(
id=trace_id,
task_description=task,
conversation=conversation,
agent_name=agent_name,
metadata_table=metadata_table,
)
results.append(trace)
return results
def detect(self, data: Any) -> bool:
items = data if isinstance(data, list) else [data]
if not items:
return False
first = items[0]
if not isinstance(first, dict):
return False
# ReAct format has "steps" with thought/action/observation
if "steps" not in first:
return False
steps = first["steps"]
if not isinstance(steps, list) or not steps:
return False
step = steps[0]
if not isinstance(step, dict):
return False
# Reject if steps have "agent" field (that's CrewAI multi-agent format)
if "agent" in step:
return False
return any(k in step for k in ("thought", "action", "observation"))