[FE/feat] Query Registry#4754
Conversation
Add a /queries page to view and manage saved trace-filter queries used by live evaluations: - New scoped @agenta/entities/query entity (create/edit/archive/unarchive, list, live match-count, matching-traces) over the Fern queries client - Query Registry dashboard with grouped table, search, duplicate, and a manage drawer that reuses the shared Filters editor inline - Toggle-able matching-traces preview reusing the observability columns + InfiniteVirtualTable shell - Active list plus a dedicated /queries/archived route with restore, mirroring the Evaluators archived-route pattern - Sidebar link, EE page stubs, and full-height layout wiring - Repoint the live-eval Online Evaluation drawer at the shared create path
Reuse the observability annotation pipeline (collect invocation links → queryAllAnnotations → attachAnnotationsToTraces) and derive evaluatorSlugs from the enriched traces, so the preview renders the evaluator-metric columns like the Observability table.
…nged applyFilter already no-ops when the draft equals the applied filter; reflect that in the button's disabled state for the always-visible inline editor (Query Registry drawer). The popover keeps Apply enabled as its primary close affordance.
…le baseline mapFilterData (props → internal) is not a clean inverse of sanitizeFilterItems, so comparing the sanitized draft against the raw filterData always read 'changed' — leaving Apply enabled on open and letting a clean draft fire onApplyFilter, which dirtied the drawer's Save too. Run both sides of the comparison through the same map → explode → sanitize pipeline so an untouched draft compares equal. Scoped to inline mode; the popover keeps Apply as its close affordance.
Queries are git-style entities (Query → Variant → Revision) with full commit/fork/revision-log on the backend, so model the edit drawer's draft state like the workflow/testset molecules instead of an ad-hoc form snapshot. - @agenta/entities/query: queryMolecule (createMolecule over the head-revision query + draft atoms), with a SEMANTIC order-insensitive isDirty (name + filtering + windowing deep-diff) so change-then-revert reads as clean, plus saveQueryHeadAtom committing a new head revision via editSimpleQuery. - Drawer: edit mode now drives the molecule (useController + reducers.update sync + saveQueryHeadAtom), replacing the snapshot-based dirty check; create stays on the one-shot path. - Unit tests for the semantic dirty diff (order-insensitivity + revert-to-clean). Revision history surfacing still depends on the backend simple-queries list returning variant_id (tracked in TODOS.md).
Each query (artifact) row in the registry expands to its earlier revisions, mirroring the workflow registry variants table (custom Name-cell toggle + tree-child rows, virtual-table-friendly), lazy-loaded on first expand. - Repoint queryQueryRevisions to query by the artifact ref (query_refs), not variant_id — simple queries are single-variant, so this is the full history and needs no variant id (which the list doesn't return). No backend change. - Revision child rows show a version badge + filter + created on/by; a loader placeholder shows while fetching; 'No earlier versions' when a query has only its head. - Per-row action hiding (ActionItem.hidden) suppresses the menu on revision rows; row-click is a no-op on them. - Corrected the stale TODOS.md note (revision history was never actually blocked on variant_id). Mechanically the expand reuses useGroupedTreeData's pattern (controlled expandedRowKeys + expandIcon: null + custom cell toggle) without the flat-revisions store rework, preserving the active/archived tabs + search + archive that operate on the artifact.
- Lazy revisions never loaded: the custom Name-cell toggle drives expansion, but the fetch was wired to antd's onExpand, which never fires when the caret is hidden (expandIcon: null). Move the fetch into the toggle's handleExpand so expanding actually fetches. - Expand toggle showed a bordered/focused box: it was a <button> (default chrome + focus ring). Switch to a plain <span onClick>, matching the workflow registry toggle. - Revision/loader rows showed a selection checkbox: override rowSelection.getCheckboxProps to disable + hide the checkbox on those rows.
… workflow variants) Make the parent row represent the latest revision, like the workflow variants table (comp-1 v5 + children v4..v1), instead of a version-less header. - Entity: add queryRevisionsForQueries — one batched queryQueryRevisions over multiple query_refs, grouped client-side by queryId (QueryRevisionSummary now carries queryId). Reuses the existing latest-revision pattern instead of a backend change. - Table: batch-fetch the visible page's revisions, enrich each head row with its head version badge + earlier-revision child rows. The expand toggle shows only when history exists; a spacer keeps single-revision rows aligned. - Replaces the lazy per-expand fetch (and its loader/empty placeholder rows) with the eager batched load.
Other git-style entities collect a commit message on update; do the same for queries. - Entity: add commitQueryRevision (git /queries/revisions/commit) which carries a message, unlike editSimpleQuery. saveQueryHeadAtom now commits via this when the head variant id is known (from the molecule's server data), falling back to editSimpleQuery otherwise; SaveQueryHeadParams gains an optional message. - Drawer: edit Save opens a lightweight commit modal (EnhancedModal + optional message textarea) instead of committing silently; create stays a one-shot. Theme-aware modal, resets with the drawer. - Test: commitQueryRevision payload contract (message + variant id).
Replace the bespoke commit modal with the reusable @agenta/entity-ui EntityCommitModal that other git-style entities use — version transition (vN → vN+1), filtering/windowing JSON diff preview, and commit message — for visual + behavioural consistency. - entity-ui: add 'query' to EntityType (+ its display-label entry) and a queryModalAdapter (getDisplayName, archive deleteAtom, commitAtom wrapping saveQueryHeadAtom, dataAtom = queryMolecule.atoms.data, commitContextAtom providing the version + diff). Registered alongside the other adapters. - Drawer: render the shared modal externally-controlled with the unwanted flows OFF — no commitModes (no save-mode/new-variant radio) and no createEntityFields (no name editing; the drawer owns the name). onSuccess refreshes the registry store + closes the drawer, solving the modal↔drawer↔list coordination.
After committing an edit, the registry's head rows refetch (paginated store invalidate), but the parent version badges + expandable revision rows come from the table's batched revision cache (React state), which was fetched once and never refreshed — so the version stayed stale and the new revision didn't appear. Add a refresh signal (queryRegistryRevisionsRefreshAtom) bumped by invalidateQueryRegistryStore; the table drops its cached revisions when it fires, so the batched fetch re-runs and the version + child rows reflect the new revision.
QueryRevisionSummary already carries the revision message; thread it onto the head + revision rows and render a 'Commit message' column (ellipsis + tooltip), mirroring the workflow variants table's Commit notes.
Mirror the workflow registry: drop the auto-created v0 initial revision from the version history (Number(version) > 0), and flag the parent (head) row as the latest with a 'Last modified' tag + dot.
Match the workflow registry, where revision rows carry per-revision actions. Revision (child) rows can now be archived via the new archiveQueryRevision (/queries/revisions/{id}/archive) — any version including the head; the parent row still archives the whole query artifact. The archive confirm modal adapts its copy (version vs query).
Archiving a revision soft-deletes it, but it had no visible home — the Archived tab is artifact-level (whole queries). Queries use soft-delete + restore (unlike the workflow registry's hard delete), so surface archived revisions inline in the version history instead of letting them vanish.
- Entity: queryRevisionsForQueries gains includeArchived (the registry passes it); QueryRevisionSummary carries deletedAt; add unarchiveQueryRevision (/queries/revisions/{id}/unarchive).
- Table: head = latest non-archived revision; archived revisions render as children tagged 'Archived' (greyed). Their action menu swaps Archive → Restore; handleRestore branches revision vs query.
- The refresh signal already re-runs the batched fetch, so archive/restore reflect immediately.
Archived revisions previously rendered inline in the active list, which was misleading (no other entity shows soft-deleted revisions among active ones). They now surface as flat, restorable rows in the Archived tab alongside archived queries; the active tab batch-fetches active revisions only.
Exercises the query data atoms and the registry's read/archive logic against a REAL running backend (no mocks): molecule head-revision fetch + isDirty round-trip, querySimpleQueries listing, batched revision history after a commit, single-revision archive/restore via the includeArchived split (the Archived-tab logic), and whole-query archive/restore active-vs-archived split. Reuses the existing ephemeral-account harness; the suite is skipIf(!hasBackend) so it skips (never passes) when no backend is configured.
The workflow switcher's 'Evaluators' group reads nonHumanEvaluatorsAtom,
which resolves is_feedback from each evaluator's latest revision — fanning
out one batched POST /workflows/revisions/query over every evaluator in the
project. That ran on sidebar mount (e.g. opening the playground) just to
render a collapsed card.
Defer the subscription until the switcher is first opened: a one-way
switcherActivated latch (set from both Dropdowns' onOpenChange) swaps the
read between nonHumanEvaluatorsAtom and a stable empty atom, so the fan-out
never mounts until needed. Reopening is served from cache. The menu={{items}}
rendering is untouched, so the sticky group-title styling is unaffected.
…ent latch The aggregate evaluator atoms (key map, meta map, non-human list, feedback schemas, full-page list) each resolve EVERY evaluator's latest revision — one batched POST /workflows/revisions/query over the whole project. Several consumers read them eagerly on mount (the playground header's evaluator picker + meta-map read, the sidebar switcher), so a plain playground load fired the whole fan-out before the user touched anything. Add a shared, one-way activation gate in evaluatorUtils: those atoms stay dormant (return stable empty values, mount no revision query) until a consumer that genuinely needs enrichment activates it. - Adapter hooks (useEvaluatorEnrichedData + the enriched evaluator adapters) activate on mount by default, so every existing evaluator picker is unchanged. A new `lazy` option opts out. - Playground: the header's 'Add evaluators' picker activates on pointer-enter/ focus (lazy); the variant-config browse adapter is lazy on app playgrounds (where it's unused) and eager on evaluator entities. A cold app-playground load no longer fires the batch. - Sidebar switcher activates on open (converged onto the shared gate). - Filters + CreateQueueDrawer activate eagerly (they need the data on mount), so observability + annotation behaviour is preserved.
…te drawer mounts Follow-up to the lazy enrichment gate. Two always-mounted consumers still fanned out the per-evaluator latest-revision batch on a plain playground load, because they read evaluator-list atoms that flow through the (ungated) evaluatorRevisionFlagsMapAtom: - The evaluator EntityPicker subscribes to the adapter's list atom on mount (even while closed) via useLevelData. The enriched evaluator adapter now holds that list empty while `lazy` and the enrichment gate is closed, so a closed picker subscribes to nothing. - AnnotateDrawer is mounted (closed) in shared layouts incl. the playground and read humanEvaluatorsListDataAtom unconditionally. It now reads the list only when `open`. Cold playground load no longer fires POST /workflows/revisions/query; the data resolves when the picker is opened / the drawer opens. Evaluation-page consumers are untouched.
…unds The evaluator template catalog (GET /evaluators/catalog/templates) was fetched on every playground load by two always-mounted readers of evaluatorTemplatesDataAtom: - PlaygroundVariantConfig read it unconditionally, though it only uses the catalog once an evaluatorKey resolves (built-in evaluator URI). Now reads the catalog only for evaluator workflows — apps skip it (mirrors the workflow molecule, which already gates its catalog read on evaluatorKey). - The enriched evaluator adapter read the templates map/data on mount even when lazy. Now holds them empty until the enrichment gate opens (same lazy condition as the list/maps), so the playground 'Add evaluators' picker fetches the catalog on open, not on mount. It's a single static, 5-min-cached request (not the per-evaluator fan-out), but this keeps a cold app-playground load free of it. Evaluator playgrounds and other pickers are unchanged.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Railway Preview Environment
|
Summary
tba...
Testing
QA follow-up
query registry page & live evals
Checklist
Contributor Resources