Skip to content

[OPIK-6525] [BE][FE] Annotation queue single-pass mode#6792

Open
miguelgrc wants to merge 6 commits into
mainfrom
miguelg/OPIK-6525-annotation-queue-single-pass
Open

[OPIK-6525] [BE][FE] Annotation queue single-pass mode#6792
miguelgrc wants to merge 6 commits into
mainfrom
miguelg/OPIK-6525-annotation-queue-single-pass

Conversation

@miguelgrc
Copy link
Copy Markdown
Contributor

Details

Implements single-pass mode for annotation queues, allowing multiple annotators to work simultaneously without re-annotating the same traces. The queue automatically serves the next unannotated item based on a configurable "annotators per item" threshold.

Backend

  • ClickHouse migration: Added annotators_per_item column (UInt16, default 1) to annotation_queues table
  • Java model/DAO: AnnotationQueue, AnnotationQueueUpdate, and AnnotationQueueDAO updated to support the new field through create, update, and find operations
  • Comment API: Create comment endpoints now return { id: commentId } in the response body (was empty 201), enabling the frontend to track comment IDs for update/delete lifecycle

Frontend

  • Queue creation form: Added "Number of annotators per item" numeric input to AddEditAnnotationQueueDialog
  • Items sidebar: New ItemsSidebar component showing all queue items with 3-state indicators (default/scored/completed), input/output previews using prettifyMessage, and scroll-to-active behavior
  • Auto-save: Scores and comments auto-save with 1s debounce via useAnnotationPersistence hook. Uses refs instead of state diffing to avoid React batching bugs. Comment creates capture server ID via onSuccess for proper update/delete lifecycle
  • Navigation: Single "Next" button navigates to next unreviewed item (Cmd+Enter shortcut). "Finish annotating" button appears when all items are done. Sidebar items are clickable for random access. All navigation flushes pending saves
  • Polling: 30s refetchInterval keeps sidebar states in sync across concurrent annotators
  • Layout: Figma-aligned redesign with card container, structured trace header bar with BaseTraceDataTypeIcon, "Annotate" panel header, and proper spacing/padding
  • Cleanup: Removed unused ValidationAlert, AnnotationTreeStateContext, old submit/previous/next buttons, and stale hotkey definitions

Change checklist

  • User facing
  • Documentation update

Issues

  • OPIK-6525

Testing

  • Tested annotation flow: add/edit/delete comments and feedback scores with auto-save
  • Tested navigation: Next button, sidebar clicks, Cmd+Enter shortcut, flush-on-navigate
  • Tested multi-state sidebar: default (outlined circle), scored (gray), completed (green)
  • Tested edge cases: create-then-immediately-edit comment, rapid navigation, polling refresh
  • Unit tests updated for AnnotationView, ReturnToAnnotationQueueButton, CompletionView (10 tests passing)

Documentation

N/A

@miguelgrc miguelgrc requested a review from a team as a code owner May 20, 2026 09:38
@github-actions github-actions Bot added java Pull requests that update Java code Frontend Backend typescript *.ts *.tsx labels May 20, 2026
@miguelgrc miguelgrc added the test-environment Deploy Opik adhoc environment label May 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔄 Test environment deployment process has started

Phase 1: Deploying base version 2.0.42-5396 (from main branch) if environment doesn't exist
Phase 2: Building new images from PR branch miguelg/OPIK-6525-annotation-queue-single-pass
Phase 3: Will deploy newly built version after build completes

You can monitor the progress here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Backend Tests - Integration Group 14

 31 files   31 suites   5m 29s ⏱️
615 tests 612 ✅ 0 💤 1 ❌ 2 🔥
609 runs  606 ✅ 0 💤 1 ❌ 2 🔥

For more details on these failures and errors, see this check.

Results for commit 541d8ba.

♻️ This comment has been updated with latest results.

Comment thread apps/opik-backend/src/main/java/com/comet/opik/api/AnnotationQueue.java Outdated
Comment thread apps/opik-frontend/src/v2/pages/SMEFlowPage/useAnnotationPersistence.ts Outdated
@CometActions
Copy link
Copy Markdown
Collaborator

Test environment is now available!

To configure additional Environment variables for your environment, run [Deploy Opik AdHoc Environment workflow] (https://github.com/comet-ml/comet-deployment/actions/workflows/deploy_opik_adhoc_env.yaml)

Access Information

The deployment has completed successfully and the version has been verified.

@miguelgrc miguelgrc force-pushed the miguelg/OPIK-6525-annotation-queue-single-pass branch from a225c5e to 977c397 Compare May 20, 2026 09:50
Comment on lines +282 to +286
queueItems.forEach((item) => {
const itemId = getAnnotationQueueItemId(item);
const state = getItemState(
item,
feedbackScoreNames,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemStates is built only from queueItems/server data and never reflects the mutated currentAnnotationState, so AnnotationView’s allDone/isNextDisabled can keep Next disabled and keep hiding Finish annotating after the last DEFAULT item. Can we fold the local annotation state into the memo or optimistically patch the computed state/comments instead of waiting on the refetch?

Finding type: Logical Bugs | Severity: 🔴 High


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages/SMEFlowPage/SMEFlowContext.tsx around lines 276-314 (the
useMemo that builds `itemStates` by iterating `queueItems` and calling `getItemState`),
`itemStates` is currently derived only from server-provided
`item.feedback_scores`/`item.comments` and ignores the local `currentAnnotationState`
that `updateComment`/`updateFeedbackScore` mutate. Refactor this computation so that
when `item.id === currentItem?.id`, you optimistically compute the state using
`currentAnnotationState` (comment text and `scores`) instead of the stale server fields,
reusing the existing `isItemProcessedByUser`/annotator count logic or feeding those
helpers a small “synthetic” item built from local state. Ensure the memo
dependencies include `currentItem`/`currentIndex` and `currentAnnotationState` (and
anything those helpers need like `annotationQueue?.feedback_definition_names` and
`currentUserName`) so `AnnotationView` updates immediately without waiting for refetch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By design — itemStates reflects server-confirmed state, not optimistic local state. The sidebar updates within ~1s (debounce fires → mutation → query invalidation → recompute). Adding a second source of truth for optimistic updates would add complexity for a barely perceptible delay. Acceptable tradeoff.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that makes sense — I'll keep this server-confirmed-state vs optimistic-state tradeoff in mind and save it to memory once the PR is merged.

Comment thread apps/opik-frontend/src/v2/pages/SMEFlowPage/AnnotationView/AnnotationView.tsx Outdated
Comment thread apps/opik-backend/src/main/java/com/comet/opik/api/AnnotationQueueUpdate.java Outdated
@CometActions
Copy link
Copy Markdown
Collaborator

🌙 Nightly cleanup: The test environment for this PR (pr-6792) has been cleaned up to free cluster resources. PVCs are preserved — re-deploy to restore the environment.

@CometActions CometActions removed the test-environment Deploy Opik adhoc environment label May 21, 2026
@miguelgrc miguelgrc added the test-environment Deploy Opik adhoc environment label May 21, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔄 Test environment deployment process has started

Phase 1: Deploying base version 2.0.42-5396 (from main branch) if environment doesn't exist
Phase 2: Building new images from PR branch miguelg/OPIK-6525-annotation-queue-single-pass
Phase 3: Will deploy newly built version after build completes

You can monitor the progress here.

@CometActions
Copy link
Copy Markdown
Collaborator

Test environment is now available!

To configure additional Environment variables for your environment, run [Deploy Opik AdHoc Environment workflow] (https://github.com/comet-ml/comet-deployment/actions/workflows/deploy_opik_adhoc_env.yaml)

Access Information

The deployment has completed successfully and the version has been verified.

@miguelgrc miguelgrc force-pushed the miguelg/OPIK-6525-annotation-queue-single-pass branch from 977c397 to e909e93 Compare May 21, 2026 23:14
Comment thread apps/opik-frontend/src/v2/pages/SMEFlowPage/AnnotationView/ItemsSidebar.tsx Outdated
Comment on lines +260 to +263
const idx = defaultItemIds.indexOf(currentItem?.id ?? "");
const nextId = defaultItemIds[(idx + 1) % defaultItemIds.length];
if (nextId === currentItem?.id) return undefined;
return queueItems.find((item) => item.id === nextId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nextDefaultItem and handleNext both scan the queue on each render/hotkey, so stepping through MAX_QUEUE_ITEMS = 15_000 can get O(n²) — should we memoize a Map<itemId, index> / Map<itemId, item> lookup?

Finding type: choose data structures | Severity: 🟢 Low


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages/SMEFlowPage/SMEFlowContext.tsx around lines 258-265 (the
`nextDefaultItem` useMemo) and lines ~289-303 (the `handleNextDefault` callback),
refactor the logic to avoid repeated `indexOf`/`find`/linear scans over large arrays
(`MAX_QUEUE_ITEMS = 15000`). Build memoized lookup structures keyed by annotation queue
item id (e.g., a Map from itemId -> index, and optionally itemId -> item) from
`queueItems`/`allItemIds`, then use direct map access to determine the next default item
and its target index in constant time. Ensure these maps are recomputed only when
`queueItems`/`defaultItemIds`/`allItemIds` change, and update the dependency arrays
accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both nextDefaultItem and the single-pass memo are useMemo — they only recompute when queueItems changes (poll every 30s), not on every render. handleNext is a useCallback that reads the already-computed nextDefaultItem. No O(n²) — it's O(n) once per poll, then O(1) lookups.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification — that makes sense given the useMemo/useCallback setup. I’ll save this to memory once the PR is merged.

@comet-ml comet-ml deleted a comment from baz-reviewer Bot May 21, 2026
@github-actions github-actions Bot added the tests Including test files, or tests related like configuration. label May 21, 2026
@comet-ml comet-ml deleted a comment from baz-reviewer Bot May 21, 2026
Comment thread apps/opik-frontend/src/v2/pages/SMEFlowPage/AnnotationView/ItemsSidebar.tsx Outdated
@CometActions
Copy link
Copy Markdown
Collaborator

🌙 Nightly cleanup: The test environment for this PR (pr-6792) has been cleaned up to free cluster resources. PVCs are preserved — re-deploy to restore the environment.

@CometActions CometActions removed the test-environment Deploy Opik adhoc environment label May 22, 2026
@miguelgrc miguelgrc added the test-environment Deploy Opik adhoc environment label May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔄 Test environment deployment process has started

Phase 1: Deploying base version 2.0.46 (from main branch) if environment doesn't exist
Phase 2: Building new images from PR branch miguelg/OPIK-6525-annotation-queue-single-pass
Phase 3: Will deploy newly built version after build completes

You can monitor the progress here.

@CometActions
Copy link
Copy Markdown
Collaborator

Test environment is now available!

To configure additional Environment variables for your environment, run [Deploy Opik AdHoc Environment workflow] (https://github.com/comet-ml/comet-deployment/actions/workflows/deploy_opik_adhoc_env.yaml)

Access Information

The deployment has completed successfully and the version has been verified.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Backend Tests - Integration Group 6

158 tests   158 ✅  3m 45s ⏱️
 27 suites    0 💤
 27 files      0 ❌

Results for commit 0e0948a.

♻️ This comment has been updated with latest results.

miguelgrc added 5 commits May 22, 2026 11:41
- Add annotators_per_item field to annotation queues (BE migration + model + DAO)
- Return comment ID in create comment response for proper update/delete lifecycle
- Redesign SME annotation view with sidebar, auto-save, and Figma-aligned layout
- Sidebar shows queue items with state indicators (default/scored/completed)
- Auto-save comments and scores with 1s debounce, flush on navigation
- Single "Next" button navigates to next unreviewed item, "Finish annotating" when done
- 30s polling keeps sidebar states in sync across annotators
- Add "Number of annotators per item" field to queue creation form
- Memoize sidebar preview text computation to avoid re-running prettifyMessage on every render
- Fix Finish annotating button not flushing pending saves before navigating to completion
- Fix comment create error not resetting lastSaved text, preventing retry
- Fix stale CREATE response corrupting next item's state after navigation (ignoreNextCreateResponseRef)
- Add @min(1) validation to annotatorsPerItem on AnnotationQueue and AnnotationQueueUpdate
- Add trailing newline to migration file
- Add 17 unit tests for useAnnotationPersistence covering comment CRUD, score operations, debounce, flush, reset, stale response handling, and error recovery
- Move item state logic (ITEM_STATE, getItemState, getDistinctAnnotatorCount, isItemProcessedByUser) from SMEFlowContext to lib/annotation-queues for reuse and testability
- Add 13 unit tests for item state computation covering all three states, annotator counting, deduplication, and score name filtering
- Unify Next button and Cmd+Enter hotkey into single handleNext callback
- Disable annotation panel (comments + feedback scores) for completed items
- Expand Opik logo text in SME page header
- Fix queue creation form number input to allow clearing (z.coerce.number)
- Add 4 BE integration tests for annotatorsPerItem: explicit value round-trip, null defaults to 1, reject 0, reject 1001
- Add @max(1000) validation to AnnotationQueue and AnnotationQueueUpdate
- Add newAnnotationQueue() test helper to ensure valid annotatorsPerItem across all 32 test sites
- Move AnnotationState type to lib/annotation-queues to break circular dependency (SMEFlowContext ↔ useAnnotationPersistence)
- Rename handleClick to handleItemClick in ItemsSidebar
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Backend Tests - Integration Group 11

 25 files   25 suites   8m 11s ⏱️
267 tests 267 ✅ 0 💤 0 ❌
246 runs  246 ✅ 0 💤 0 ❌

Results for commit 0e0948a.

♻️ This comment has been updated with latest results.

@miguelgrc miguelgrc force-pushed the miguelg/OPIK-6525-annotation-queue-single-pass branch from 1198460 to 0e0948a Compare May 22, 2026 09:41
@miguelgrc miguelgrc added test-environment Deploy Opik adhoc environment and removed test-environment Deploy Opik adhoc environment labels May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔄 Test environment deployment process has started

Phase 1: Deploying base version 2.0.47-5432 (from main branch) if environment doesn't exist
Phase 2: Building new images from PR branch miguelg/OPIK-6525-annotation-queue-single-pass
Phase 3: Will deploy newly built version after build completes

You can monitor the progress here.

Comment thread apps/opik-frontend/src/v2/pages/SMEFlowPage/SMEFlowContext.tsx
Comment on lines +152 to +154
(item.comments || []).forEach((comment) => {
if (comment.created_by) authors.add(comment.created_by);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we gate getDistinctAnnotatorCount on comments_enabled (or pass the flag in), otherwise legacy comments can still satisfy annotators_per_item and make getItemState return COMPLETED too early?

Finding type: Logical Bugs | Severity: 🔴 High


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/lib/annotation-queues.ts around lines 137-157, the
getDistinctAnnotatorCount function currently adds every comment.created_by into the
authors Set unconditionally. Update this logic so comment authors are only counted when
comments are enabled: either add a commentsEnabled boolean parameter to
getDistinctAnnotatorCount (and thread it through getItemState/isItemProcessedByUser as
needed) or filter item.comments before invoking these helpers. Ensure that when
comments_enabled=false, legacy/non-scored comments cannot cause getItemState to return
ITEM_STATE.COMPLETED before any score-based annotations are made.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By design — comments_enabled controls whether the UI shows the comment form, not whether existing comments count as annotations. If someone commented before comments were disabled, that review should still count toward completion. Un-completing previously-done items would be a worse UX.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that makes sense. I'll remember this after the PR is merged.

@CometActions
Copy link
Copy Markdown
Collaborator

Test environment is now available!

To configure additional Environment variables for your environment, run [Deploy Opik AdHoc Environment workflow] (https://github.com/comet-ml/comet-deployment/actions/workflows/deploy_opik_adhoc_env.yaml)

Access Information

The deployment has completed successfully and the version has been verified.

…asn't annotated

- Completed + user not an annotator: hide comments/scores, show check icon + message
- Completed + user is an annotator: show editable comments/scores
- Not completed: normal editable UI
@miguelgrc miguelgrc added test-environment Deploy Opik adhoc environment and removed test-environment Deploy Opik adhoc environment labels May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔄 Test environment deployment process has started

Phase 1: Deploying base version 2.0.47-5432 (from main branch) if environment doesn't exist
Phase 2: Building new images from PR branch miguelg/OPIK-6525-annotation-queue-single-pass
Phase 3: Will deploy newly built version after build completes

You can monitor the progress here.

@CometActions
Copy link
Copy Markdown
Collaborator

Test environment is now available!

To configure additional Environment variables for your environment, run [Deploy Opik AdHoc Environment workflow] (https://github.com/comet-ml/comet-deployment/actions/workflows/deploy_opik_adhoc_env.yaml)

Access Information

The deployment has completed successfully and the version has been verified.

@CometActions
Copy link
Copy Markdown
Collaborator

🌙 Nightly cleanup: The test environment for this PR (pr-6792) has been cleaned up to free cluster resources. PVCs are preserved — re-deploy to restore the environment.

@CometActions CometActions removed the test-environment Deploy Opik adhoc environment label May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend Frontend java Pull requests that update Java code tests Including test files, or tests related like configuration. typescript *.ts *.tsx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants