Skip to content

fix ai rendering#2376

Merged
ehayes2000 merged 1 commit intomainfrom
fix-ai-rendering
Apr 6, 2026
Merged

fix ai rendering#2376
ehayes2000 merged 1 commit intomainfrom
fix-ai-rendering

Conversation

@ehayes2000
Copy link
Copy Markdown
Contributor

No description provided.

@ehayes2000 ehayes2000 requested a review from a team as a code owner April 6, 2026 16:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f617d283-1b5b-4302-a8ea-1b0be0d7081c

📥 Commits

Reviewing files that changed from the base of the PR and between 704ba71 and 0f46260.

📒 Files selected for processing (1)
  • js/app/packages/core/component/AI/component/message/AssistantMessage.tsx

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of assistant message parts to ensure consistent access and stronger type safety.
    • Tightened null/empty checks to prevent intermittent rendering errors for tool, tool response, and text segments.
    • Results in more reliable message display and fewer edge-case failures when streaming or composing assistant responses.

Walkthrough

Replaced per-iteration currentPart value with a factory/thunk (() => part()) inside AssistantMessageParts. All null checks, type discriminations, casts, and property accesses were adjusted to call the thunk (e.g., currentPart()!.type) and closures now read from currentPart().

Changes

Cohort / File(s) Summary
AssistantMessage Component Logic
js/app/packages/core/component/AI/component/message/AssistantMessage.tsx
Changed currentPart from a direct value to a thunk () => part(). Updated null/empty guard to if (!currentPart()) return null;, and updated all downstream type checks, casts, and property accesses to invoke the thunk (e.g., currentPart()!.type, currentPart()! as ...).
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No description was provided by the author, making it impossible to assess relatedness to the changeset. Add a description explaining the purpose of changing currentPart handling from a resolved value to a thunk/factory function and its impact on rendering.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix ai rendering' follows the conventional commits format with 'fix:' prefix (implied) and is 16 characters, well under the 72-character limit. It clearly describes the change.

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


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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

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

Caution

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

⚠️ Outside diff range comments (1)
js/app/packages/core/component/AI/component/message/AssistantMessage.tsx (1)

373-429: 🧹 Nitpick | 🔵 Trivial

Consider using match from ts-pattern for exhaustive type handling.

The if-else chain on the discriminated union AssistantMessagePart could use match for cleaner, exhaustive pattern matching. This would also surface the unhandled toolCallErr variant (see relevant snippets showing 4 variants exist).

♻️ Proposed refactor using `match`
+import { match } from 'ts-pattern';

Then replace the if-else chain:

-        if (currentPart()!.type === 'toolCall') {
-          const toolCall = () =>
-            currentPart()! as Extract<AssistantMessagePart, { type: 'toolCall' }>;
-
-          return (
-            <RenderTool
-              tool_id={toolCall().id}
-              chat_id={'todo'}
-              json={toolCall().json}
-              name={toolCall().name}
-              message_id={props.message.id}
-              part_index={i()}
-              type="call"
-              isComplete={isCompleteSelector(toolCall().id)}
-              renderContext={{
-                renderContext: {
-                  isStreaming: props.isStreaming,
-                },
-              }}
-            />
-          );
-        } else if (currentPart()!.type === 'toolCallResponseJson') {
-          const toolResponse = () =>
-            currentPart()! as Extract<
-              AssistantMessagePart,
-              { type: 'toolCallResponseJson' }
-            >;
-
-          return (
-            <RenderTool
-              isComplete={true}
-              tool_id={toolResponse().id}
-              chat_id={'todo'}
-              json={toolResponse().json}
-              name={toolResponse().name}
-              message_id={props.message.id}
-              part_index={i()}
-              type="response"
-              renderContext={{
-                renderContext: {
-                  isStreaming: props.isStreaming,
-                },
-              }}
-            />
-          );
-        } else if (currentPart()!.type === 'text') {
-          const textPart = () =>
-            currentPart()! as Extract<AssistantMessagePart, { type: 'text' }>;
-
-          if (textPart().text.trim().length > 0)
-            return (
-              <ChatMessageMarkdown
-                text={textPart().text}
-                generating={() => props.isStreaming}
-              />
-            );
-        }
+        return match(currentPart()!)
+          .with({ type: 'toolCall' }, (toolCall) => (
+            <RenderTool
+              tool_id={toolCall.id}
+              chat_id={'todo'}
+              json={toolCall.json}
+              name={toolCall.name}
+              message_id={props.message.id}
+              part_index={i()}
+              type="call"
+              isComplete={isCompleteSelector(toolCall.id)}
+              renderContext={{
+                renderContext: {
+                  isStreaming: props.isStreaming,
+                },
+              }}
+            />
+          ))
+          .with({ type: 'toolCallResponseJson' }, (toolResponse) => (
+            <RenderTool
+              isComplete={true}
+              tool_id={toolResponse.id}
+              chat_id={'todo'}
+              json={toolResponse.json}
+              name={toolResponse.name}
+              message_id={props.message.id}
+              part_index={i()}
+              type="response"
+              renderContext={{
+                renderContext: {
+                  isStreaming: props.isStreaming,
+                },
+              }}
+            />
+          ))
+          .with({ type: 'text' }, (textPart) =>
+            textPart.text.trim().length > 0 ? (
+              <ChatMessageMarkdown
+                text={textPart.text}
+                generating={() => props.isStreaming}
+              />
+            ) : null
+          )
+          .with({ type: 'toolCallErr' }, () => null) // Explicitly handle error variant
+          .exhaustive();

As per coding guidelines: "For exhaustive switch statements in TypeScript, use match from ts-pattern".

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

In `@js/app/packages/core/component/AI/component/message/AssistantMessage.tsx`
around lines 373 - 429, The current if/else chain in AssistantMessage.tsx using
currentPart() to discriminate AssistantMessagePart is not exhaustive and misses
the toolCallErr variant; replace the chain with ts-pattern's match (import {
match } from 'ts-pattern') on currentPart() to perform exhaustive pattern
matching over AssistantMessagePart (cases: toolCall, toolCallResponseJson, text,
toolCallErr, etc.), map each case to the same JSX you currently return (use
RenderTool, ChatMessageMarkdown, and isCompleteSelector where appropriate,
preserving props.message.id, part_index i(), and props.isStreaming), and ensure
the toolCallErr case is handled (render an error UI or RenderTool with error
state) so the compiler/runtime know all variants are covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@js/app/packages/core/component/AI/component/message/AssistantMessage.tsx`:
- Around line 370-371: The current code defines a redundant wrapper const
currentPart = () => part() and then calls currentPart(); replace this
indirection by using the existing memo accessor directly (either call part()
where needed or assign const currentPart = part) to simplify the logic in
AssistantMessage.tsx; update any references that call currentPart() to call
part() (or use the direct accessor variable) and remove the extra arrow-function
wrapper introduced around the mapArray part accessor.

---

Outside diff comments:
In `@js/app/packages/core/component/AI/component/message/AssistantMessage.tsx`:
- Around line 373-429: The current if/else chain in AssistantMessage.tsx using
currentPart() to discriminate AssistantMessagePart is not exhaustive and misses
the toolCallErr variant; replace the chain with ts-pattern's match (import {
match } from 'ts-pattern') on currentPart() to perform exhaustive pattern
matching over AssistantMessagePart (cases: toolCall, toolCallResponseJson, text,
toolCallErr, etc.), map each case to the same JSX you currently return (use
RenderTool, ChatMessageMarkdown, and isCompleteSelector where appropriate,
preserving props.message.id, part_index i(), and props.isStreaming), and ensure
the toolCallErr case is handled (render an error UI or RenderTool with error
state) so the compiler/runtime know all variants are covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 763630e7-a445-4e95-a56d-4aaa60f0cf5f

📥 Commits

Reviewing files that changed from the base of the PR and between 89c34c3 and 704ba71.

📒 Files selected for processing (1)
  • js/app/packages/core/component/AI/component/message/AssistantMessage.tsx

Comment on lines +370 to +371
const currentPart = () => part();
if (!currentPart()) return null;
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

Simplify the aliasing: currentPart just wraps part redundantly.

Since part from mapArray is already a memo accessor, const currentPart = () => part() adds an unnecessary layer of indirection. Consider using part directly or assigning const currentPart = part;.

🔧 Proposed simplification
-        const currentPart = () => part();
-        if (!currentPart()) return null;
+        const currentPart = part;
+        if (!currentPart()) return 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
const currentPart = () => part();
if (!currentPart()) return null;
const currentPart = part;
if (!currentPart()) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/core/component/AI/component/message/AssistantMessage.tsx`
around lines 370 - 371, The current code defines a redundant wrapper const
currentPart = () => part() and then calls currentPart(); replace this
indirection by using the existing memo accessor directly (either call part()
where needed or assign const currentPart = part) to simplify the logic in
AssistantMessage.tsx; update any references that call currentPart() to call
part() (or use the direct accessor variable) and remove the extra arrow-function
wrapper introduced around the mapArray part accessor.

@ehayes2000 ehayes2000 merged commit 143d213 into main Apr 6, 2026
22 checks passed
@ehayes2000 ehayes2000 deleted the fix-ai-rendering branch April 6, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant