<tool name="set_spans_filter">
  <description>Apply a declarative filter to the Phoenix spans table so the user (and you) are looking at the subset of spans relevant to the current question.</description>
  <when_to_use>
    - The user asks to focus on a kind of span (LLM calls, tool calls, errored spans, slow spans, hallucinated spans, etc.).
    - You need to narrow the table before drilling into a specific failure, latency outlier, or annotation.
    - The user asks to clear or reset the current filter.
  </when_to_use>
  <call_contract>
    - Every call sets the table to a complete state. `condition` (the freeform Phoenix span filter DSL) describes the entire view, root-span scoping included. There is no notion of leaving part of the filter unchanged — always send the whole condition.
    - Pass `condition` as an empty string to clear the filter and show every span.
  </call_contract>
  <root_spans_are_part_of_the_condition>
    - Restrict to root spans -- spans with no parent -- with the predicate `parent_id is None`; omit it to search every span. This is usually the top-level span of each trace, but nothing enforces one root per trace: a fragmented or partially-ingested trace can have several, so do not treat the row count as a trace count. The spans table starts at `parent_id is None`, so dropping that predicate is how you widen the view.
    - `parent_span is None` is the wider root predicate: it also matches orphans, spans whose parent was never ingested. Use it when the user asks about orphaned spans.
    - Do NOT add a root predicate when narrowing by a `span_kind` other than CHAIN or AGENT (LLM, TOOL, RETRIEVER, EMBEDDING, RERANKER, EVALUATOR, GUARDRAIL) — those kinds are almost always nested under a CHAIN/AGENT root, so root-scoping them yields zero results even when matching spans exist.
    - Same applies when filtering by anything that targets nested spans (specific tool names, `status_code == 'ERROR'` on inner calls, annotations attached to leaf spans).
  </root_spans_are_part_of_the_condition>
  <dsl_reference>
    - String fields: `span_kind`, `name` (the span name, NOT span_name), `status_code`, `status_message`, `span_id`, `trace_id`, `parent_id`. Compare with `==`, `!=`, or membership via `in`.
    - Numeric fields: `latency_ms`, `cumulative_token_count.{prompt,completion,total}`, `llm.token_count.{prompt,completion,total}`. Compare with `<`, `<=`, `>`, `>=`.
    - Datetime fields: `start_time`, `end_time`. Resolve relative phrases like "last week" or "past 5 days" against the value returned by the `get_current_datetime` tool — call it first. Never guess the current date from prior knowledge.
    - Attribute access: `attributes['key.path']`, `input.value`, `output.value`, `metadata['key']`.
    - Annotations: `annotations['Name'].label`, `annotations['Name'].score`.
    - Substring search: `'needle' in input.value` (any string field, ignores case).
    - Combine clauses with `and`, `or`, `not`.
  </dsl_reference>
  <value_conventions>
    - `span_kind` values are UPPERCASE string literals: 'LLM', 'TOOL', 'CHAIN', 'AGENT', 'RETRIEVER', 'EMBEDDING', 'RERANKER', 'EVALUATOR', 'GUARDRAIL', 'UNKNOWN'. Lowercase will not match.
    - `status_code` values are UPPERCASE: 'OK', 'ERROR', 'UNSET'.
    - Always wrap string literals in single or double quotes.
  </value_conventions>
  <examples>
    - "Show me LLM spans" → `span_kind == 'LLM'`.
    - "Tool calls" → `span_kind == 'TOOL'`.
    - "Errored spans" → `status_code == 'ERROR'`.
    - "Slow LLM calls" → `span_kind == 'LLM' and latency_ms >= 5000`.
    - "Top-level traces only" → `parent_id is None`.
    - "Slow traces" → `parent_id is None and latency_ms >= 5000`.
    - "Include orphaned spans as roots" → `parent_span is None`.
    - "Hallucinations" → `annotations['Hallucination'].label == 'hallucinated'`.
    - "Spans mentioning agent in input" → `'agent' in input.value`.
    - "Show everything" → `''`.
    - "Reset to default view" → `parent_id is None`.
  </examples>
</tool>
