filter-expressions
A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.
Claude Code
> /plugin marketplace add arize-ai/phoenix> /plugin install arize-phoenix@arize-phoenix
Cursor
Cursor also loads subagents from .claude/agents, so copy this file there to use it in Cursor.
Agent definition
filter-expressions.mdFilter expressions
A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.
| Language | Argument | Keeps | Accepted on | | --- | --- | --- | --- | | Span filter | `filterCondition` | individual spans | `Project.spans`, `Trace.spans`, project aggregates (`recordCount`, `tokenCountTotal`, `costSummary`, `latencyMsQuantile`, ...), `SpanQuery().where(...)` in the Python client, the UI spans filter bar | | Trace filter | `traceFilterCondition` | every span of a matching trace | `Project.spans`, the UI traces filter bar, the `filter` query param on `GET /v1/projects/{id}/traces` and the `filter` argument of `client.traces.get_traces(...)` (Python) and `getTraces({...})` (TypeScript) | | Session filter | `sessionFilterCondition` | sessions | `Project.sessions`, project aggregates, the UI sessions filter bar, the `filter` query param on `GET /v1/projects/{id}/sessions` and the `filter` argument of `client.sessions.list(...)` (Python) and `listSessions({...})` (TypeScript) |
`filterCondition` and `traceFilterCondition` compose on `Project.spans`: matching spans inside matching traces. `filterCondition` and `sessionFilterCondition` are mutually exclusive on the aggregates that accept both.
Checking a condition
| Need | GraphQL field on `Project` | | --- | --- | | Validate before running | `validateSpanFilterCondition(condition:)`, `validateTraceFilterCondition(condition:)`, `validateSessionFilterCondition(condition:)` → `{ isValid errorMessage }` | | List bindable names | `traceFilterVocabulary`, `sessionFilterVocabulary` → `{ name type category description iterableName }` | | Span filter names | no vocabulary field; the table under [Span filter](#span-filter) is exhaustive |
A span filter that returns no rows may be misspelled rather than unmatched. Check the spelling against the vocabulary before concluding there is no data.
Syntax shared by all three languages
Operators
| Category | Accepted | Rejected | | --- | --- | --- | | Comparison | `==` `!=` `<` `<=` `>` `>=`; chained: `500 < latency_ms <= 2000` | `=` | | Missing values | `is None`, `is not None` | `is null`, `null`, `is` with any other value | | Membership | `x in [...]` exact; `'text' in field` case-insensitive substring; `not in` | `None` inside a list; a literal on the left (`1 in [1, 2]`); `like` | | Logic | `and`, `or`, `not`, parentheses | `&&`, `\|\|`, `&`, `\|`, `!` | | Arithmetic | `+` `-` `*` `/` `%` | `**`, `//`, bitwise operators | | Casts | `float(x)`, `int(x)`, `str(x)` | `bool(x)`, any other function | | Comprehensions | `any`, `all`, `len`, `sum`, `max`, `min` over a declared collection: `any(d.cost > 0 for d in cost_details)`; `len` takes a list comprehension | method calls (`name.startswith(...)`), `len(name)` |
Literals
| Kind | Write | Not | | --- | --- | --- | | String | `'LLM'` or `"LLM"` | unquoted: `LLM` | | Number | `100`, `0.5` | quoted: `'100'` | | Boolean | `True`, `False` | `true`, `false` | | Missing | `None` | `null`, `nil` | | Datetime | ISO 8601 with an offset: `'2026-09-01T00:00:00Z'`, `'2026-09-01T00:00:00+00:00'` | without an offset | | List | `['LLM', 'TOOL']`, same type throughout | mixed types; `None` elements |
Rules that differ from Python
- A missing value fails every comparison, including `!=`. A span without `metadata['tier']`
matches neither `metadata['tier'] == 'premium'` nor `metadata['tier'] != 'premium'`. Spell the missing case out: `metadata['tier'] != 'premium' or metadata['tier'] is None`.
- `is None` is true for an absent key and for a stored JSON `null`.
- Every operand of `and`, `or`, and `not` must itself be a condition. `name == 'x' and
metadata['flag']` is rejected; write `metadata['flag'] == True`.
- The whole expression must be a condition. A bare `True` or a bare field is rejected.
- `'text' in field` ignores case. `==` and list membership are exact.
- Annotation accessors expose `.label` (string), `.score` (number), `.explanation` (string), and
`.identifier` (string). The bare accessor is an existence check: `annotations['quality']`.
- `.identifier` is never missing: it is `''` on an annotation written without one, so test for
that with `== ''` rather than `is None`.
Span filter
Root spans
There is no `traces` connection and no root-span argument. Root-span scoping is a clause in `filterCondition`, and it composes with everything else. The UI's traces table is `spans(filterCondition: "parent_span is None", traceFilterCondition: ...)`.
| Clause | Keeps | Use when | | --- | --- | --- | | `parent_id is None` | spans with no parent id | the default | | `parent_span is None` | those, plus orphans whose parent span was never received | you want every top-level span, including ones whose parent was dropped |
A root span is usually one per trace. Fragmented traces can have several.
Vocabulary
This table is exhaustive. Every identifier not in it is read as an attribute path.
| Name | Type | Notes | | --- | --- | --- | | `span_id`, `trace_id`, `parent_id` | string | OpenTelemetry hex ids | | `name` | string | span name | | `span_kind` | enum | `'CHAIN'`, `'LLM'`, `'RETRIEVER'`, `'EMBEDDING'`, `'TOOL'`, `'AGENT'`, `'RERANKER'`, `'GUARDRAIL'`, `'EVALUATOR'`, `'PROMPT'`, `'DECISION'`, `'UNKNOWN'`; literals are uppercased for you | | `status_code` | enum | `'OK'`, `'ERROR'`, `'UNSET'`; literals are uppercased for you | | `status_message` | string | error text | | `latency_ms` | number | | | `start_time`, `end_time` | datetime | | | `cumulative_llm_token_count_prompt`, `cumulative_llm_token_count_completion`, `cumulative_llm_token_count_total` | number | this span plus its descendants | | `llm.token_count.prompt`, `llm.token_count.completion`, `llm.token_count.total` | number | this span alone | | `total_cost`, `prompt_cost`, `completion_cost` | number | `0` whe
Read more
Filter expressions
A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.
| Language | Argument | Keeps | Accepted on | | --- | --- | --- | --- | | Span filter | `filterCondition` | individual spans | `Project.spans`, `Trace.spans`, project aggregates (`recordCount`, `tokenCountTotal`, `costSummary`, `latencyMsQuantile`, ...), `SpanQuery().where(...)` in the Python client, the UI spans filter bar | | Trace filter | `traceFilterCondition` | every span of a matching trace | `Project.spans`, the UI traces filter bar, the `filter` query param on `GET /v1/projects/{id}/traces` and the `filter` argument of `client.traces.get_traces(...)` (Python) and `getTraces({...})` (TypeScript) | | Session filter | `sessionFilterCondition` | sessions | `Project.sessions`, project aggregates, the UI sessions filter bar, the `filter` query param on `GET /v1/projects/{id}/sessions` and the `filter` argument of `client.sessions.list(...)` (Python) and `listSessions({...})` (TypeScript) |
`filterCondition` and `traceFilterCondition` compose on `Project.spans`: matching spans inside matching traces. `filterCondition` and `sessionFilterCondition` are mutually exclusive on the aggregates that accept both.
Checking a condition
| Need | GraphQL field on `Project` | | --- | --- | | Validate before running | `validateSpanFilterCondition(condition:)`, `validateTraceFilterCondition(condition:)`, `validateSessionFilterCondition(condition:)` → `{ isValid errorMessage }` | | List bindable names | `traceFilterVocabulary`, `sessionFilterVocabulary` → `{ name type category description iterableName }` | | Span filter names | no vocabulary field; the table under [Span filter](#span-filter) is exhaustive |
A span filter that returns no rows may be misspelled rather than unmatched. Check the spelling against the vocabulary before concluding there is no data.
Syntax shared by all three languages
Operators
| Category | Accepted | Rejected | | --- | --- | --- | | Comparison | `==` `!=` `<` `<=` `>` `>=`; chained: `500 < latency_ms <= 2000` | `=` | | Missing values | `is None`, `is not None` | `is null`, `null`, `is` with any other value | | Membership | `x in [...]` exact; `'text' in field` case-insensitive substring; `not in` | `None` inside a list; a literal on the left (`1 in [1, 2]`); `like` | | Logic | `and`, `or`, `not`, parentheses | `&&`, `\|\|`, `&`, `\|`, `!` | | Arithmetic | `+` `-` `*` `/` `%` | `**`, `//`, bitwise operators | | Casts | `float(x)`, `int(x)`, `str(x)` | `bool(x)`, any other function | | Comprehensions | `any`, `all`, `len`, `sum`, `max`, `min` over a declared collection: `any(d.cost > 0 for d in cost_details)`; `len` takes a list comprehension | method calls (`name.startswith(...)`), `len(name)` |
Literals
| Kind | Write | Not | | --- | --- | --- | | String | `'LLM'` or `"LLM"` | unquoted: `LLM` | | Number | `100`, `0.5` | quoted: `'100'` | | Boolean | `True`, `False` | `true`, `false` | | Missing | `None` | `null`, `nil` | | Datetime | ISO 8601 with an offset: `'2026-09-01T00:00:00Z'`, `'2026-09-01T00:00:00+00:00'` | without an offset | | List | `['LLM', 'TOOL']`, same type throughout | mixed types; `None` elements |
Rules that differ from Python
- A missing value fails every comparison, including `!=`. A span without `metadata['tier']`
matches neither `metadata['tier'] == 'premium'` nor `metadata['tier'] != 'premium'`. Spell the missing case out: `metadata['tier'] != 'premium' or metadata['tier'] is None`.
- `is None` is true for an absent key and for a stored JSON `null`.
- Every operand of `and`, `or`, and `not` must itself be a condition. `name == 'x' and
metadata['flag']` is rejected; write `metadata['flag'] == True`.
- The whole expression must be a condition. A bare `True` or a bare field is rejected.
- `'text' in field` ignores case. `==` and list membership are exact.
- Annotation accessors expose `.label` (string), `.score` (number), `.explanation` (string), and
`.identifier` (string). The bare accessor is an existence check: `annotations['quality']`.
- `.identifier` is never missing: it is `''` on an annotation written without one, so test for
that with `== ''` rather than `is None`.
Span filter
Root spans
There is no `traces` connection and no root-span argument. Root-span scoping is a clause in `filterCondition`, and it composes with everything else. The UI's traces table is `spans(filterCondition: "parent_span is None", traceFilterCondition: ...)`.
| Clause | Keeps | Use when | | --- | --- | --- | | `parent_id is None` | spans with no parent id | the default | | `parent_span is None` | those, plus orphans whose parent span was never received | you want every top-level span, including ones whose parent was dropped |
A root span is usually one per trace. Fragmented traces can have several.
Vocabulary
This table is exhaustive. Every identifier not in it is read as an attribute path.
| Name | Type | Notes | | --- | --- | --- | | `span_id`, `trace_id`, `parent_id` | string | OpenTelemetry hex ids | | `name` | string | span name | | `span_kind` | enum | `'CHAIN'`, `'LLM'`, `'RETRIEVER'`, `'EMBEDDING'`, `'TOOL'`, `'AGENT'`, `'RERANKER'`, `'GUARDRAIL'`, `'EVALUATOR'`, `'PROMPT'`, `'DECISION'`, `'UNKNOWN'`; literals are uppercased for you | | `status_code` | enum | `'OK'`, `'ERROR'`, `'UNSET'`; literals are uppercased for you | | `status_message` | string | error text | | `latency_ms` | number | | | `start_time`, `end_time` | datetime | | | `cumulative_llm_token_count_prompt`, `cumulative_llm_token_count_completion`, `cumulative_llm_token_count_total` | number | this span plus its descendants | | `llm.token_count.prompt`, `llm.token_count.completion`, `llm.token_count.total` | number | this span alone | | `total_cost`, `prompt_cost`, `completion_cost` | number | `0` whe
Other agents on phoenix.
More agents from other plugins.
cavecrew-builder
AgentSurgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…
Questions about filter-expressions.
- Which coding agents can use filter-expressions?
- filter-expressions installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.

