This is a submission for the Hermes Agent Challenge.
After a 50-task literature review run I had 3,000 JSONL events. I wanted the tool calls that had errors. Then I wanted only the llm_call events from the supervisor lane. Then the events whose name matched ^web_. Every time I reached for a new filter I was writing a new list comprehension.
That's trace-grep.
One call
from trace_grep import grep_events, grep_file, grep_text, grep_field
events = load_my_events()
# Full-text search across all fields
matches = grep_text(events, "timeout")
# Exact field match
matches = grep_field(events, "kind", "tool_call")
# Substring match in a specific field
matches = grep_field(events, "name", contains="search")
# Regex match
matches = grep_field(events, "name", regex=r"^web_")
# Has error
matches = grep_events(events, has_error=True)
# Invert (find non-matching events)
matches = grep_field(events, "kind", "tool_call", invert=True)
From a file
from trace_grep import grep_file
matches = grep_file("logs/run.jsonl", pattern="error", has_error=True)
Combine criteria (AND)
Every argument you pass becomes another filter. All must match.
matches = grep_events(
events,
field="kind", value="tool_call",
has_error=True,
)
# tool calls that also have an error field
GrepMatch
Every result is a GrepMatch — the full event, its original index, and which fields matched:
@dataclass
class GrepMatch:
event: dict # the full event
index: int # 0-based position in the input list
matched_fields: dict # {field: value} for matched fields
So you can still read every field on the original event, and you know exactly where it came from.
error detection
has_error=True checks for any of error, err, or exception — covers the naming differences across agent frameworks:
# find everything that failed
failures = grep_events(events, has_error=True)
for m in failures:
print(m.event.get("name"), m.event.get("error"))
has_error=False returns events without any error field — useful to confirm the happy path.
What I actually do with it
# How many tool calls errored?
errors = grep_events(events, field="kind", value="tool_call", has_error=True)
# Which web tools fired?
web_tools = grep_field(events, "name", regex=r"^web_")
# Everything NOT from the supervisor lane
workers = grep_field(events, "lane", regex=r"worker-\d+")
# Cost events only — exact float match
expensive = grep_events(events, field="cost_usd", value=0.05)
No custom parser needed per filter.
Zero dependencies
Standard library only: re, json, pathlib, dataclasses. Nothing to install beyond the package.
pip install trace-grep
Top comments (0)