Skip to content

feat(dashboard): align chat bubbles to request/response sides and add…#135

Merged
SantiagoDePolonia merged 4 commits intomainfrom
feature/interactions-improvements
Mar 12, 2026
Merged

feat(dashboard): align chat bubbles to request/response sides and add…#135
SantiagoDePolonia merged 4 commits intomainfrom
feature/interactions-improvements

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented Mar 12, 2026

… collapsible function notes

User/request messages positioned left, agent/response messages right to reflect audit log request-response flow. Function calls (from AI) align right, function results (from client) align left. Function notes are now collapsible via

Details — showing one-line summary by default with expandable full content.

Summary by CodeRabbit

  • New Features

    • Display tool calls and function results inline with messages
    • Collapsible function details with structured metadata and expanded view
    • Footer area listing tool calls for applicable messages
  • Style

    • Updated chat-bubble alignment (user left, assistant/error right), backgrounds, and visual hierarchy
    • Enhanced styling for tool/function notes, typography, and caveat icon affordance
  • Chores

    • Renamed “Conversation” to “Interactions” and updated related UI copy and labels

… collapsible function notes

User/request messages positioned left, agent/response messages right to
reflect audit log request-response flow. Function calls (from AI) align
right, function results (from client) align left. Function notes are now
collapsible via <details> — showing one-line summary by default with
expandable full content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 12, 2026

Warning

Rate limit exceeded

@SantiagoDePolonia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 52003e97-f5e1-44c6-9dc6-735bbc62dd5a

📥 Commits

Reviewing files that changed from the base of the PR and between 80369d0 and 0ea4b49.

📒 Files selected for processing (2)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js
📝 Walkthrough

Walkthrough

Renames the admin "conversation" UI to "interactions", adds client-side mapping/attachment of tool call IDs to messages, and introduces UI and CSS to render collapsible function-call/function-result notes and a tool-call list in the audit drawer.

Changes

Cohort / File(s) Summary
Styles (chat/tool/function UI)
internal/admin/dashboard/static/css/dashboard.css
Reworked chat-bubble alignment for roles (user/assistant/error); added .chat-tool-calls, .chat-tool-call, .chat-function-note and inner/detail/expanded variants plus monospace typography and caveat icon tweaks. Note: duplicated blocks present.
Conversation drawer logic
internal/admin/dashboard/static/js/modules/conversation-drawer.js
Added call-id collection (_collectCallIds), tool-call extraction (_extractToolCalls), and functionExpandedContent; extended _conversationMessage signature to include toolCalls and functionName; recognize function_call/function_result roles; attach toolCalls/functionName across request/response/request-input/response-output flows; updated error copy to "interactions."
Templates / UI text & structure
internal/admin/dashboard/templates/index.html
Renamed UI copy, IDs and aria labels from "conversation" → "interactions"; render function_call/function_result with .chat-function-note and collapsible details/summary; include tool-calls footer; update empty/loading texts.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser
  participant Template as Server Template
  participant DrawerJS as conversation-drawer.js
  participant AuditAPI as Audit Response
  Browser->>Template: open admin page / click audit trigger
  Browser->>DrawerJS: request interactions for entry
  DrawerJS->>AuditAPI: fetch audit entry (request/response bodies)
  AuditAPI-->>DrawerJS: return entry (includes tool call ids)
  DrawerJS->>DrawerJS: _collectCallIds(map, requestBody, responseBody)
  DrawerJS->>DrawerJS: _extractToolCalls(collectedCallIds)
  DrawerJS->>Browser: emit messages enriched with toolCalls + functionName
  Browser->>Template: render messages (chat bubbles, .chat-function-note, tool-calls footer)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I nibbled through logs where function sparks sing,
Tool-call lightning, a tidy little thing.
Notes that fold and answers that bloom,
Interactions now fit in a cozy room. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is truncated ('feat(dashboard): align chat bubbles to request/response sides and add…') and does not fully convey the complete change. However, it accurately captures the primary change: aligning chat bubbles to request/response sides. The ellipsis indicates the title was cut off, but the visible portion is specific and directly related to the main changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/interactions-improvements

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/admin/dashboard/static/js/modules/conversation-drawer.js (1)

183-193: ⚠️ Potential issue | 🟠 Major

Preserve requestBody.input order in a single pass.

This block emits all messages from extractResponsesInputMessages(...) first and then appends every function_call_output. If requestBody.input mixes normal messages and function outputs, the reconstructed interaction order is wrong.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/modules/conversation-drawer.js` around
lines 183 - 193, The code currently calls
extractResponsesInputMessages(requestBody.input) first and then separately
iterates requestBody.input to append function_call_output items, which breaks
original ordering when inputs mix types; change the logic to do a single pass
when requestBody.input is an array: iterate requestBody.input once and for each
item, if item is a function_call_output produce the function_result message (use
extractText for non-string outputs and use item.call_id or '' for call id) and
otherwise pass the item into extractResponsesInputMessages (or extract its
messages) and push those messages via this._conversationMessage to the messages
array; keep the existing behavior for non-array requestBody.input by still
calling extractResponsesInputMessages(requestBody.input). Ensure you still
increment idx for every pushed message and use the same fields (m.role, m.text,
ts, entry.id, isAnchor) and the same creation of function_result messages (role
'function_result', text, ts, entry.id, isAnchor, ++idx, [], item.call_id || '').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/css/dashboard.css`:
- Around line 1421-1459: The anchor border-color is being overridden because
.chat-function-note.is-anchor and .chat-function-note.role-function-* share
equal specificity; add more specific rules that target both states together
(e.g. .chat-function-note.is-anchor.role-function-call and
.chat-function-note.is-anchor.role-function-result) and set the desired
border-color (the same color-mix used in .chat-function-note.is-anchor) so
anchored function notes retain the anchor border color even when role classes
are present.

In `@internal/admin/dashboard/static/js/modules/conversation-drawer.js`:
- Around line 165-167: The UI is using m.name (which is not provided) to label
tool results, so change the logic that builds the message label in
conversation-drawer.js (where this._conversationMessage is called for role ===
'tool' and in the other affected blocks) to resolve the function name by
matching m.tool_call_id or m.call_id against the original call records (the
tool_calls[] entries or the function_call item on the corresponding response
choice) instead of using m.name; implement a small helper (e.g.,
getFunctionNameFromCallId(callId)) that searches the conversation/response
metadata for the matching tool_call entry or function_call and returns its name,
then pass that name into this._conversationMessage (falling back to an empty
string only if no match is found).

In `@internal/admin/dashboard/templates/index.html`:
- Line 492: The Close button lacks an explicit type which makes it default to
"submit" and may inadvertently submit a surrounding form; update the button
element (x-ref="conversationCloseBtn", `@click`="closeConversation()") to include
type="button" so it won't trigger form submission when clicked.
- Around line 515-520: The x-for loop uses a non-unique key (:key="tc.name")
which can break Alpine's DOM diffing when the same tool is called multiple
times; update the template to include the loop index (e.g., change x-for to "
(tc, idx) in (msg.toolCalls || [])" ) and make the key unique (e.g.,
:key="tc.name + '-' + idx" ), or use a unique identifier on the tool-call
objects (tc.id) if available, ensuring each chat-tool-call row has a truly
unique :key.

---

Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/conversation-drawer.js`:
- Around line 183-193: The code currently calls
extractResponsesInputMessages(requestBody.input) first and then separately
iterates requestBody.input to append function_call_output items, which breaks
original ordering when inputs mix types; change the logic to do a single pass
when requestBody.input is an array: iterate requestBody.input once and for each
item, if item is a function_call_output produce the function_result message (use
extractText for non-string outputs and use item.call_id or '' for call id) and
otherwise pass the item into extractResponsesInputMessages (or extract its
messages) and push those messages via this._conversationMessage to the messages
array; keep the existing behavior for non-array requestBody.input by still
calling extractResponsesInputMessages(requestBody.input). Ensure you still
increment idx for every pushed message and use the same fields (m.role, m.text,
ts, entry.id, isAnchor) and the same creation of function_result messages (role
'function_result', text, ts, entry.id, isAnchor, ++idx, [], item.call_id || '').

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 561ef3ba-efbb-416e-bf46-147351cdeb8a

📥 Commits

Reviewing files that changed from the base of the PR and between 72866c0 and 535b6fd.

📒 Files selected for processing (3)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js
  • internal/admin/dashboard/templates/index.html

- Fix CSS specificity so anchor border-color is not overridden by
  role-specific function note styles
- Resolve tool result function names by matching tool_call_id against
  call records instead of relying on m.name
- Add type="button" to Close button to prevent implicit form submission
- Use indexed key in tool-calls x-for loop to avoid duplicate keys
- Process requestBody.input in a single pass to preserve original
  ordering of messages and function_call_output items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/templates/index.html`:
- Line 502: The article element that renders function notes (conditional classes
using msg.role === 'function_call' || msg.role === 'function_result' and
classNames msg.roleClass / msg.isAnchor) lacks a screen-reader label; update the
<article> rendered for 'chat-function-note' to include an appropriate ARIA
attribute (e.g., aria-label or aria-labelledby, or role="region" + aria-label)
that describes it as a function call/result (for example "Function call details"
or include the function name from the message data) so assistive tech can
announce the purpose of the collapsible <details> content; keep the attribute
conditional only for messages with msg.role === 'function_call' || msg.role ===
'function_result'.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 66c3216f-3532-4a19-8215-21f9b10d41d7

📥 Commits

Reviewing files that changed from the base of the PR and between 535b6fd and 50a3ad9.

📒 Files selected for processing (3)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js
  • internal/admin/dashboard/templates/index.html

<template x-for="msg in conversationMessages" :key="msg.uid">
<article class="chat-message" :class="[msg.roleClass, msg.isAnchor ? 'is-anchor' : '']">
<header class="chat-message-meta">
<article :class="[msg.role === 'function_call' || msg.role === 'function_result' ? 'chat-function-note' : 'chat-message', msg.roleClass, msg.isAnchor ? 'is-anchor' : '']">
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.

🧹 Nitpick | 🔵 Trivial

Consider accessibility: missing role/ARIA attributes on function note article.

The article element conditionally becomes either a .chat-function-note or .chat-message based on role. For screen reader users, the nested <details> inside .chat-function-note is accessible, but consider adding aria-label to clarify the function note's purpose since it lacks a visible header like regular messages.

♿ Optional accessibility improvement
-                    <article :class="[msg.role === 'function_call' || msg.role === 'function_result' ? 'chat-function-note' : 'chat-message', msg.roleClass, msg.isAnchor ? 'is-anchor' : '']">
+                    <article :class="[msg.role === 'function_call' || msg.role === 'function_result' ? 'chat-function-note' : 'chat-message', msg.roleClass, msg.isAnchor ? 'is-anchor' : '']"
+                             :aria-label="(msg.role === 'function_call' || msg.role === 'function_result') ? msg.roleLabel : null">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<article :class="[msg.role === 'function_call' || msg.role === 'function_result' ? 'chat-function-note' : 'chat-message', msg.roleClass, msg.isAnchor ? 'is-anchor' : '']">
<article :class="[msg.role === 'function_call' || msg.role === 'function_result' ? 'chat-function-note' : 'chat-message', msg.roleClass, msg.isAnchor ? 'is-anchor' : '']"
:aria-label="(msg.role === 'function_call' || msg.role === 'function_result') ? msg.roleLabel : null">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/templates/index.html` at line 502, The article
element that renders function notes (conditional classes using msg.role ===
'function_call' || msg.role === 'function_result' and classNames msg.roleClass /
msg.isAnchor) lacks a screen-reader label; update the <article> rendered for
'chat-function-note' to include an appropriate ARIA attribute (e.g., aria-label
or aria-labelledby, or role="region" + aria-label) that describes it as a
function call/result (for example "Function call details" or include the
function name from the message data) so assistive tech can announce the purpose
of the collapsible <details> content; keep the attribute conditional only for
messages with msg.role === 'function_call' || msg.role === 'function_result'.

SantiagoDePolonia and others added 2 commits March 12, 2026 12:57
…delegating to helper

extractResponsesInputMessages was designed for the full input, not
individual items. Process function_call, function_call_output, and
role-based message items explicitly in the single-pass loop, falling
back to the helper only for non-array inputs (plain strings).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add overflow:hidden to body via conversation-drawer-open class toggled
on open/close. Make #interactions-drawer-content the scrollable region
with flex:1 and overflow-y:auto so the drawer scrolls independently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/admin/dashboard/static/js/modules/conversation-drawer.js (1)

303-316: 🧹 Nitpick | 🔵 Trivial

Consider using an options object for optional parameters.

The signature now has 8 positional parameters, with the last two (toolCalls, functionName) being optional. This works correctly but may become harder to maintain as more fields are added.

♻️ Optional refactor using options object
-            _conversationMessage(role, text, timestamp, entryID, isAnchor, idx, toolCalls, functionName) {
+            _conversationMessage(role, text, timestamp, entryID, isAnchor, idx, opts = {}) {
+                const { toolCalls, functionName } = opts;
                 const normalized = this._roleMeta(role);
                 return {
                     uid: entryID + '-' + idx,

Then callers would use:

this._conversationMessage('function_result', text, ts, entry.id, isAnchor, ++idx, { functionName: fnName })
this._conversationMessage('function_call', '', ts, entry.id, isAnchor, ++idx, { toolCalls: [...] })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d55ff2fd-d4f1-410e-b0ab-b1038993df4e

📥 Commits

Reviewing files that changed from the base of the PR and between 50a3ad9 and 80369d0.

📒 Files selected for processing (1)
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js

@SantiagoDePolonia SantiagoDePolonia merged commit 4899d25 into main Mar 12, 2026
13 checks passed
@SantiagoDePolonia SantiagoDePolonia deleted the feature/interactions-improvements branch March 22, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant