[OPIK-5622] [FE] feat: revamp agent configuration version UI and edit flow#6219
Conversation
… flow Introduce a shared CollapsibleField component (with expand-all/collapse-all controller) used across the agent configuration detail view, the new side-panel edit flow, and the agent sandbox. Single-line values render expanded; multi-line values collapse by default. On the configuration page, editing a version now opens an 872px side panel with a sticky header, notes textarea, collapsible fields, and a sticky action bar. The diff view is toggled in-place within the same panel (Show diff / Back to edit) and "Save as new version" works from either view. Version notes truncate with Show more in the read-only detail view. In the sandbox, the Configuration tab adopts the same field component, shows an "Unsaved changes" tag, and exposes an explicit "Save configuration" action which surfaces a confirmation toast linking back to the Agent configuration page. Obsolete SaveVersionDialog and unused BlueprintDiffDialog wrapper removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure field rendering to match the Figma hierarchy: the outer
field header (type icon + name + info) now sits without card chrome on
the surface, while bordered CollapsibleBlocks act as the collapsible
units underneath. Chat prompts render each message (System, Prompt,
etc.) as its own labeled CollapsibleBlock via a new
BlueprintValuePromptCompact + BlueprintChatMessages component pair, so
agent-configuration views no longer reuse the playground prompt chrome.
Apply remaining UI feedback: gray icons on Deploy to / Edit
configuration, "Deploy to..." copy with circle-fading-arrow-up icon,
in-place Show diff / Back to edit toggle with git-compare and undo-2
icons, "Compare {name} -> current changes" header, "Add version notes"
textarea above the Edit fields row, 48px panel header, save
configuration button with save + arrow-up-right icons, "Edited"
indicator with red dot replacing the orange tag, save toast wired to
the new blueprint id so it reads "You have created {versionName}",
focus-within highlight on collapsible blocks, scrollbar-gutter stable
on the configuration page, descriptions always shown in the diff
table, bare scalar editors (no card) for non collapsible fields in
edit mode, and a broadcast-driven Expand all / Collapse all that also
fans out to prompt chat messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nt-config-version-ui-improvements # Conflicts: # apps/opik-frontend/src/v2/pages/AgentConfigurationPage/AgentConfigurationTab/AgentConfigurationTab.tsx
|
🔄 Test environment deployment process has started Phase 1: Deploying base version You can monitor the progress here. |
| const descriptionIsLong = description.length > DESCRIPTION_TRUNCATE_LENGTH; | ||
|
|
||
| const collapsibleKeys = useMemo( | ||
| () => collectMultiLineKeys(agentConfig?.values ?? []), | ||
| [agentConfig], | ||
| ); | ||
| const hasExpandableFields = useMemo( | ||
| () => hasAnyExpandableField(agentConfig?.values ?? []), | ||
| [agentConfig], | ||
| ); | ||
| const collapseController = useFieldsCollapse({ collapsibleKeys }); | ||
|
|
There was a problem hiding this comment.
Since useFieldsCollapse({ collapsibleKeys }) only seeds expandedKeys/broadcast once, should we reset the collapseController (or its expandedKeys/broadcast) when collapsibleKeys changes so expanded sections and allExpanded don’t carry over across AgentConfigurationDetailView versions?
Finding type: Logical Bugs | Severity: 🟢 Low
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages/AgentConfigurationPage/AgentConfigurationTab/AgentConfigurationDetailView.tsx
around lines 82-97 (the `collapsibleKeys`/`hasExpandableFields` useMemo and
`collapseController = useFieldsCollapse({ collapsibleKeys })` logic), the collapse
controller is stateful and isn’t reset when `agentConfig` changes. Update the
implementation so that when `collapsibleKeys` changes (i.e., switching versions), the
controller’s `expandedKeys` and the expand-all `broadcast`/`allExpanded` state are
reinitialized from the new keys (e.g., by making `useFieldsCollapse` watch
`collapsibleKeys` and reset its internal state, or by forcing a remount/key change of
the controller based on the current `agentConfig`/`collapsibleKeys`). Verify that
expanded sections do not carry over between versions and that the expand-all toggle
reflects only the current version’s fields.
There was a problem hiding this comment.
Skipping — the detail view receives a new selectedItem when switching versions, which causes agentConfig to change and collapsibleKeys to recompute via useMemo. The useFieldsCollapse hook starts with an empty expandedKeys set, so state doesn't carry over in practice. If a future refactor caches the controller across versions, this would need revisiting.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Got it — thanks for the clarification. I’ll save this to memory once the PR is merged.
| const title = useMemo(() => `New agent configuration`, []); | ||
|
|
There was a problem hiding this comment.
title is wrapped in useMemo even though it’s a hard-coded string literal—should we drop useMemo and use const title = "New agent configuration" instead?
Finding type: AI Coding Guidelines | Severity: 🟢 Low
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages-shared/agent-configuration/AgentConfigurationEditPanel.tsx
around lines 57-58, the `title` value is a hard-coded string but is wrapped in
`useMemo`, which violates the guideline that memoization should be reserved for complex
computations. Remove the `useMemo` usage and replace it with a plain constant `const
title = "New agent configuration";`. Also update the import to drop `useMemo` from the
React import if it becomes unused.
There was a problem hiding this comment.
Fixed in 65d68e2 — removed useMemo wrapper, now a plain const.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Commit 65d68e2 addressed this comment by removing the unnecessary useMemo and replacing it with a plain const title = "New agent configuration". The React import was also cleaned up to drop useMemo now that it is unused.
| const allExpanded = useMemo(() => { | ||
| if (broadcast.action === "expand") return true; | ||
| if (broadcast.action === "collapse") return false; | ||
| if (collapsibleKeys.length === 0) return false; | ||
| return collapsibleKeys.every((k) => expandedKeys.has(k)); | ||
| }, [broadcast.action, collapsibleKeys, expandedKeys]); |
There was a problem hiding this comment.
Since allExpanded short-circuits to true when broadcast.action === "expand" but toggle never updates broadcast, collapsing one field can leave broadcast.action stuck and make the UI think everything is expanded — should we clear broadcast.action when toggling individual keys or fall back to checking expandedKeys instead?
Finding type: Design extensible API interfaces | Severity: 🟠 Medium
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages-shared/agent-configuration/fields/useFieldsCollapse.ts
around lines 61-66 and the `toggle` logic around lines 39-49, fix the stale
`broadcast.action` behavior that makes `allExpanded` return true/false incorrectly.
Either update `toggle` to reset `broadcast.action` (e.g., set it to null and increment
version) whenever an individual key is toggled, or refactor `allExpanded` to not
short-circuit on `broadcast.action` and instead always derive the value from
`expandedKeys` (e.g., compare every `collapsibleKeys` entry). Ensure that after a user
collapses one field, `allExpanded` immediately reflects the real expanded state and
header/button UI stays consistent.
There was a problem hiding this comment.
Fixed in 65d68e2 — toggle() now resets broadcast.action to null so allExpanded immediately reflects the real expanded state after individual toggles.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Thanks for the update — that makes sense. I’ll save this to memory once the PR is merged.
There was a problem hiding this comment.
Commit 65d68e2 addressed this comment by resetting broadcast.action to null (and bumping the version) within toggle, ensuring allExpanded no longer short-circuits on a stale broadcast when collapsing individual keys.
|
✅ 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. |
|
🌙 Nightly cleanup: The test environment for this PR ( |
…ting - Panel header + "Edit fields" title: comet-body-accented (16px) instead of 14px - Expand/collapse icon inherits button text color (no longer looks disabled) - Detail view: override text-foreground on expand/collapse wrapper - Editable prompts: white card background, borderless auto-sizing textarea - Spacing: 1rem between textarea and Edit fields heading Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nt-config-version-ui-improvements # Conflicts: # apps/opik-frontend/src/v2/pages-shared/traces/ConfigurationTab/BlueprintValuesList.tsx # apps/opik-frontend/src/v2/pages/AgentConfigurationPage/AgentConfigurationTab/AgentConfigurationDetailView.tsx # apps/opik-frontend/src/v2/pages/AgentConfigurationPage/AgentConfigurationTab/AgentConfigurationTab.tsx
- Remove useMemo on hardcoded title string (#2) - Reset broadcast.action to null on individual toggle so allExpanded doesn't get stuck (#3) - Preserve multimodal content array in handleChangeMessage — only update text parts, keep image/video/audio intact (#5) - Extract AutoResizeTextarea component to deduplicate inline auto-sizing logic from BlueprintChatMessages and BlueprintValuePromptCompact (#6) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| const ref = useCallback((el: HTMLTextAreaElement | null) => { | ||
| if (el) { | ||
| el.style.height = "auto"; | ||
| el.style.height = el.scrollHeight + "px"; | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
el.style.height is only recomputed in the ref callback, so controlled value updates in BlueprintValuePromptCompact can leave the textarea at the initial height and clip until typing—should we recompute height in an effect keyed on value?
Finding type: Logical Bugs | Severity: 🟠 Medium
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages-shared/agent-configuration/fields/AutoResizeTextarea.tsx
around lines 18-23 (the `ref` callback that sets `el.style.height`), the textarea only
recalculates height on mount/ref, so later controlled `value` updates won’t resize.
Refactor by storing the textarea element in a ref (instead of relying solely on the
callback) and adding a `useLayoutEffect` (or `useEffect`) that runs whenever `value`
changes to set `style.height` to `auto` and then `scrollHeight + "px"`. Ensure the same
resizing logic is used for both user typing and programmatic value updates.
There was a problem hiding this comment.
Fixed in 839c66c — AutoResizeTextarea now uses a useEffect keyed on value to recalculate height, so controlled value changes (e.g. from expand-all broadcast) resize the textarea correctly without requiring a keystroke.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Commit 839c66c addressed this comment by storing the textarea in a ref, centralizing the resize logic, and running it inside a useEffect that depends on value so controlled updates now recompute the height.
There was a problem hiding this comment.
Thanks for the update — that fix addresses the controlled-value resize case. I’ll save this to memory once the PR is merged.
|
🔄 Test environment deployment process has started Phase 1: Deploying base version You can monitor the progress here. |
|
✅ 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. |
Editable chat messages now support: role selector dropdown (System / User / Assistant), move up/down reordering, duplicate, delete, and "Add message" button. CollapsibleBlock gains trailing and headerPrefix props to host these controls in the header row. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| const EDITABLE_ROLES = [ | ||
| LLM_MESSAGE_ROLE.system, | ||
| LLM_MESSAGE_ROLE.user, | ||
| LLM_MESSAGE_ROLE.assistant, | ||
| ]; |
There was a problem hiding this comment.
EDITABLE_ROLES only lists system/user/assistant, so prompts loaded via parseChatTemplateToLLMMessages() that include ai/tool_execution_result don’t match any SelectItem and can’t be labeled/edited—should we add those roles to the dropdown or fall back to an editable editor?
Finding type: Logical Bugs | Severity: 🟠 Medium
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages-shared/agent-configuration/fields/BlueprintChatMessages.tsx
around lines 35-39, update the EDITABLE_ROLES constant to include all roles defined by
LLM_MESSAGE_ROLE (specifically the missing ai and tool_execution_result) so the role
dropdown can render SelectItem entries for any role coming from
parseChatTemplateToLLMMessages(). Also verify getRoleLabel(...) handles these roles; if
it doesn’t, add a label fallback so the SelectTrigger always displays something for
loaded prompts. If you can’t safely support those roles in the dropdown, refactor the
role editor to fall back to a read-only label + an “unknown role” editor path rather
than rendering a blank trigger.
There was a problem hiding this comment.
Skipping — agent configurations only use system/user/assistant roles. The ai and tool_execution_result roles are playground-specific and wouldn't appear in agent config prompts. If a prompt with exotic roles is loaded, the label falls back to capitalize(role) which is readable enough.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Got it — in agent configuration prompts only system/user/assistant roles are expected here, so the broader role handling isn't needed. I'll remember this after the PR is merged.
| const dragHandle = | ||
| editable && messages.length > 1 ? ( | ||
| <button | ||
| className="cursor-grab touch-none text-light-slate hover:text-foreground active:cursor-grabbing" | ||
| {...listeners} | ||
| {...attributes} | ||
| > | ||
| <GripHorizontal className="size-3.5" /> |
There was a problem hiding this comment.
The drag handle is a <button> without type, so in a <form> it defaults to type="submit" and can trigger form submission—should we set type="button"?
Finding type: Component responsibility | Severity: 🟢 Low
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/v2/pages-shared/agent-configuration/fields/BlueprintChatMessages.tsx
around lines 117-124 within the SortableMessage component, the drag handle renders a
plain <button> without an explicit type attribute, which defaults to type="submit" when
placed inside a form. Refactor by adding type="button" to that drag-handle button
element so clicking/dragging it can’t trigger unintended parent form submissions.
Ensure the rest of the drag props ({...listeners} and {...attributes}) remain unchanged.
There was a problem hiding this comment.
Fixed in 839c66c — added type="button" to the drag handle to prevent accidental form submission.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Commit 839c66c addressed this comment by adding type="button" to the drag-handle button so it no longer defaults to submit, while leaving the drag listeners and attributes unchanged.
| const sensors = useSensors( | ||
| useSensor(MouseSensor, { activationConstraint: { distance: 5 } }), | ||
| ); | ||
|
|
||
| const handleDragEnd = useCallback( | ||
| (event: DragEndEvent) => { | ||
| const { active, over } = event; | ||
| if (!over || active.id === over.id || !onReorder) return; | ||
|
|
There was a problem hiding this comment.
BlueprintChatMessages duplicates the DnD useSensors/DndContext + arrayMove reorder logic and handleDragEnd from LLMPromptMessages, so should we reuse the shared reorder handler or extract a hook instead of copying it?
Finding type: Code Dedup and Conventions | Severity: 🟢 Low
Want Baz to fix this for you? Activate Fixer
There was a problem hiding this comment.
Skipping — the DnD pattern uses @dnd-kit library primitives (DndContext, SortableContext, useSortable, arrayMove). There's no project-specific shared abstraction to extract — LLMPromptMessages uses the same library calls inline. Extracting a shared hook for ~10 lines of boilerplate would add indirection without meaningful dedup.
🤖 Reply posted via /address-github-pr-comments
There was a problem hiding this comment.
Got it — that makes sense for this @dnd-kit usage, and I’ll save this to memory once the PR is merged.
…rag handle - AutoResizeTextarea: useEffect keyed on value recalculates height so external value changes (e.g. expand all) don't clip content - Drag handle button: add type="button" to prevent form submission Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…toast - Button label: "Message" instead of "Add message" (plus icon provides context) - Remove toast from config page save (only sandbox shows toast per spec) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
🔄 Test environment deployment process has started Phase 1: Deploying base version You can monitor the progress here. |
|
✅ 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. |
| }; | ||
|
|
||
| const ExpandAllToggle: React.FC<ExpandAllToggleProps> = ({ | ||
| controller, |
There was a problem hiding this comment.
why wouldn't pass controller.allExpanded here and have it is a boolean?
Details
Screen.Recording.2026-04-13.at.18.21.01.mov
Some styling updates follow UX review:
Screen.Recording.2026-04-14.at.9.44.40.mov
Overhaul the agent-configuration version UI and edit flow for the 2.0 release
(OPIK-5622) — aligning field rendering, edit/diff flow, and the sandbox
Configuration tab with the Figma spec.
Field rendering
FieldSection(type icon + name + info tooltip) sits on the surface with no card chrome.CollapsibleBlockunits hold the values underneath.CollapsibleBlockvia newBlueprintValuePromptCompact+BlueprintChatMessages— the agent-configuration surfaces no longer reuse the playground chrome.Edit side panel
Sheet).From v{N}tag (pencil icon) + close.Show diff(git-compare) /Back to edit(undo-2) buttons.Agent sandbox — Configuration tab
Editedindicator (red dot) replaces the orange "Unsaved changes" tag.Save configurationaction with save + arrow-up-right icons.Read-only detail view
scrollbar-gutter: stableso fold/unfold doesn't shift page layout.Change checklist
Issues
AI-WATERMARK
AI-WATERMARK: yes
Testing
npm run typechecknpm run lintEditedindicator, Save configuration, success toast with navigation linkDocumentation
N/A — no public-facing SDK / API / docs changes.