Skip to content

Conversation

@dylan-eustice
Copy link
Contributor

@dylan-eustice dylan-eustice commented Sep 26, 2025

Description

Fixes yarn build linter errors. Built on top of streaming data branch.

By Submitting this PR I confirm:

  • I am familiar with the Contributing Guidelines.
  • We require that all contributors "sign-off" on their commits. This certifies that the contribution is your original work, or you have rights to submit it under the same license, or a compatible license.
    • Any contribution which contains commits that are not Signed-Off will not be accepted.
  • When the PR is ready for review, new or existing tests cover these changes.
  • When the PR is ready for review, the documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Live Data Streaming panel with multi‑stream selection and real-time indicators; toggle available in chat header.
    • Database Updates page to review finalized entries with filtering, status, sorting, and auto‑refresh.
    • Context‑Aware RAG chat pathway.
    • Global theme provider (light/dark) with persistent settings.
  • Improvements

    • More reliable chat streaming, improved auto‑scroll and user toasts.
    • Enhanced Markdown rendering (tables, math, raw HTML).
  • Documentation

    • New Live Data Streaming guide and README updates.
  • Chores

    • Build-time support for NEXT_PUBLIC variables; settings now persist in localStorage.

Add getBackendURL to map endpoints

Add parsing for CA RA response

Update README with CA RAG backend option
Add toggle for data stream display

Add environment variable for data stream toggle
Make the theme update cross-tab (local -> session storage)
Toggle background with underlying div
Reword 'transcript' references to 'entries'

Minor typo

Add pending / ingested toggle in database updates page
@copy-pr-bot
Copy link

copy-pr-bot bot commented Sep 26, 2025

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai
Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds Live Data Streaming API, UI components, and history page; introduces ThemeContext and wraps the app; adds CA‑RAG control flow in chat API; updates chat and markdown components, types, and settings storage (sessionStorage → localStorage); Docker and _document script handling adjusted; many minor import/prop hygiene edits.

Changes

Cohort / File(s) Summary
Documentation
DATA_STREAMING.md, README.md
New DATA_STREAMING.md and README additions documenting Live Data Streaming feature, API endpoints, UI toggle, and CA‑RAG reference.
Build & Env Script
Dockerfile, pages/_document.tsx
Adds NEXT_PUBLIC build ARGs/ENV in Docker build stage; replaces inline /__ENV.js with Next Script (beforeInteractive).
Theming
contexts/ThemeContext.tsx, pages/_app.tsx, components/Chat/ChatHeader.tsx, components/Settings/SettingDialog.tsx
Adds ThemeProvider/useTheme, wraps app, and replaces previous lightMode dispatches with setLightMode; header and settings use theme.
Live Data Streaming (API + UI + State)
pages/api/update-data-stream.ts, components/DataStreamDisplay/*, components/Chat/DataStreamControls.tsx, components/DataStreamDisplay/DataStreamManager.tsx, pages/database-updates.tsx, pages/api/home/home.state.tsx, pages/api/home/home.tsx
New in-memory multi-stream API (POST/GET/PATCH); DataStreamDisplay and manager UI with polling and stream selector; DataStreamControls toggle; database-updates history page; adds dataStreams and showDataStreamDisplay state and integrates stream selection into Home.
Chat API — CA‑RAG
pages/api/chat.ts, types/chat.ts
Adds CA‑RAG pathway: per-conversation init, payload builder, processing for CA‑RAG responses; maps frontend CA‑RAG path to backend call. Adds selectedStream? to Conversation type.
Chat Component Changes
components/Chat/Chat.tsx, components/Chat/ChatInput.tsx, components/Chat/ChatHeader.tsx, components/Chat/ChatMessage.tsx, components/Chat/ChatInteractionMessage.tsx, components/Chat/MemoizedChatMessage.tsx, components/Chat/Regenerate.tsx
Significant refactor: safer streaming/parse handling, uuid/toast additions, scroll/user interaction handling, some prop/param renames, markdown plugin additions, header wiring updated to include DataStreamControls.
Chatbar / Sidebar / Props Hygiene
components/Chatbar/*, components/Sidebar/Sidebar.tsx, components/Search/Search.tsx, components/Folder/Folder.tsx, components/Settings/Import.tsx, pages/api/home/home.context.tsx
Many import cleanups, unused param renames (underscore-prefixed), minor prop signature parameter-name updates, and removed unused context imports.
Markdown Component Refactors
components/Markdown/CustomComponents.tsx, components/Markdown/*
Extracts memoized custom components, adds isEqual memo guards, cleans unused icon/context imports, aliases memoized components with displayName while preserving behavior.
Avatar / Spinner / Misc Cleanups
components/Avatar/*, components/Spinner/Spinner.tsx
Removes unused imports and minor formatting; no behavioral changes.
Settings Storage
utils/app/settings.ts
Switches getSettings/saveSettings persistence from sessionStorage to localStorage.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Home as Home (page)
  participant Manager as DataStreamManager
  participant Display as DataStreamDisplay
  participant API as /api/update-data-stream
  participant DBPage as /database-updates

  User->>Home: Toggle Data Stream Display
  Home->>Manager: mount/update (selectedConversation)
  Manager->>Display: render with selectedStream(s)
  loop Poll live text (100ms)
    Display->>API: GET ?stream={id}
    API-->>Display: { text }
    Display->>Display: update UI, autoscroll
  end
  loop Poll streams (2s)
    Manager->>API: GET
    API-->>Manager: [streams]
    Manager->>Home: update dataStreams (if changed)
  end
  DBPage->>API: GET ?type=finalized
  API-->>DBPage: [finalized entries]
  DBPage->>API: PATCH / update pending (by uuid)
Loading
sequenceDiagram
  autonumber
  participant Client as Client UI
  participant API as /api/chat
  participant Backend as CA‑RAG Backend

  Client->>API: POST chat (chatCompletionURL = /chat/ca-rag, conversationId)
  API->>API: buildContextAwareRAGPayload(conversationId)
  API->>Backend: (if not initialized) POST /init { id: ragUuid+conversationId }
  Backend-->>API: 200 OK
  API->>Backend: POST /call with built payload
  Backend-->>API: response (result/content)
  API-->>Client: normalized chat response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I twitch my nose at streams that flow,
Bits and bops in gentle glow—
Themes to flip, histories to view,
RAG wakes up and chats anew.
A hop, a patch, the data sings—🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title “Linter fixes” does not summarize the primary changes in this PR, which introduce a live data streaming API, new UI components for data streams, context updates, and related feature integrations rather than merely lint adjustments. Please update the pull request title to clearly reflect the main scope, for example “Add live data streaming API and UI integration” instead of “Linter fixes.”
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@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 (5)
pages/_document.tsx (1)

1-26: Do not use next/script in _document.

next/script is unsupported inside a custom Document and will trigger Next's @next/next/no-script-in-document error (“next/script cannot be used in next/document”). Switch back to a plain <script> tag (or move the script to _app if you need the Script component API) to keep the build from breaking.

-import Script from 'next/script';
...
-        <Script src="/__ENV.js" strategy="beforeInteractive" />
+        <script src="/__ENV.js"></script>
components/Chat/ChatInteractionMessage.tsx (1)

3-28: Reset modal state when dismissed

userInput and error now live across modal openings. If a user closes after triggering a validation error, the old text and error resurface the next time the modal opens. Reset the local state when isOpen flips to false so each interaction starts clean. This also requires adding useEffect to the import.

-import { useState } from 'react';
+import { useEffect, useState } from 'react';
…
   const [userInput, setUserInput] = useState('');
   const [error, setError] = useState('');
+
+  useEffect(() => {
+    if (!isOpen) {
+      setUserInput('');
+      setError('');
+    }
+  }, [isOpen]);
utils/app/settings.ts (1)

9-22: Guard localStorage usage against SSR

localStorage is undefined during SSR/build. getSettings() is invoked in contexts/ThemeContext.tsx at module init (Line 19) before the provider mounts, so on the server this will throw and break page rendering. Please gate both reads/writes with typeof window !== 'undefined', falling back to defaults when not in a browser.

Apply this diff:

-export const getSettings = (): Settings => {
+export const getSettings = (): Settings => {
   let settings: Settings = {
     theme: 'light',
   };
-  const settingsJson = localStorage.getItem(STORAGE_KEY);
+  if (typeof window === 'undefined') {
+    return settings;
+  }
+
+  const settingsJson = window.localStorage.getItem(STORAGE_KEY);
   if (settingsJson) {
     try {
       let savedSettings = JSON.parse(settingsJson) as Settings;
       settings = Object.assign(settings, savedSettings);
     } catch (e) {
       console.error(e);
     }
   }
   return settings;
 };
 
 export const saveSettings = (settings: Settings) => {
-  localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
+  if (typeof window === 'undefined') {
+    return;
+  }
+
+  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
 };
components/Markdown/Chart.tsx (1)

3-31: Mark Chart and its consumers as client components
Add "use client" as the very first line in components/Markdown/Chart.tsx and in any file importing it (e.g. components/Markdown/CustomComponents.tsx).

components/Chat/ChatMessage.tsx (1)

15-339: Do not render unsanitized raw HTML

Adding rehypeRaw means we now inject raw HTML coming straight from message.content into the DOM. Any user can paste something like <img src=x onerror=alert(1)>, and React will pass that onerror attribute through, triggering an XSS when the message renders. Assistant messages can echo the same payload, so this is exploitable both ways. We need to sanitize before shipping.

Please either drop rehypeRaw or pair it with a sanitizer (e.g., rehype-sanitize configured to allow only the tags/attributes we actually need, such as details, summary, and math markup).

-import rehypeRaw from 'rehype-raw';
-import remarkGfm from 'remark-gfm';
-import remarkMath from 'remark-math';
+import rehypeRaw from 'rehype-raw';
+import rehypeSanitize from 'rehype-sanitize';
+import remarkGfm from 'remark-gfm';
+import remarkMath from 'remark-math';-                      rehypePlugins={[rehypeRaw] as any}
+                      rehypePlugins={[rehypeRaw, rehypeSanitize] as any}-                      rehypePlugins={[rehypeRaw] as any}
+                      rehypePlugins={[rehypeRaw, rehypeSanitize] as any}

Be sure to extend the sanitize schema so the components you rely on keep rendering, but event-handler attributes and other dangerous markup stay blocked.

🧹 Nitpick comments (1)
components/DataStreamDisplay/DataStreamDisplay.tsx (1)

98-100: Remove leftover timestamp debug log

This console log fires on every finalized-data fetch and quickly drowns the DevTools console. Please drop it once the debugging session is over.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc982f6 and 0f4d904.

📒 Files selected for processing (45)
  • DATA_STREAMING.md (1 hunks)
  • Dockerfile (1 hunks)
  • README.md (1 hunks)
  • components/Avatar/SystemAvatar.tsx (1 hunks)
  • components/Avatar/UserAvatar.tsx (0 hunks)
  • components/Chat/Chat.tsx (12 hunks)
  • components/Chat/ChatHeader.tsx (5 hunks)
  • components/Chat/ChatInput.tsx (9 hunks)
  • components/Chat/ChatInteractionMessage.tsx (1 hunks)
  • components/Chat/ChatMessage.tsx (2 hunks)
  • components/Chat/MemoizedChatMessage.tsx (1 hunks)
  • components/Chat/Regenerate.tsx (0 hunks)
  • components/Chatbar/Chatbar.context.tsx (1 hunks)
  • components/Chatbar/Chatbar.tsx (1 hunks)
  • components/Chatbar/components/ChatFolders.tsx (0 hunks)
  • components/Chatbar/components/ChatbarSettings.tsx (1 hunks)
  • components/Chatbar/components/ClearConversations.tsx (0 hunks)
  • components/Chatbar/components/Conversation.tsx (0 hunks)
  • components/DataStreamDisplay/DataStreamDisplay.tsx (1 hunks)
  • components/Folder/Folder.tsx (1 hunks)
  • components/Markdown/Chart.tsx (1 hunks)
  • components/Markdown/CodeBlock.tsx (1 hunks)
  • components/Markdown/CustomComponents.tsx (2 hunks)
  • components/Markdown/CustomDetails.tsx (1 hunks)
  • components/Markdown/CustomSummary.tsx (1 hunks)
  • components/Markdown/Image.tsx (2 hunks)
  • components/Markdown/Loading.tsx (0 hunks)
  • components/Markdown/MemoizedReactMarkdown.tsx (1 hunks)
  • components/Markdown/Video.tsx (2 hunks)
  • components/Search/Search.tsx (1 hunks)
  • components/Settings/Import.tsx (2 hunks)
  • components/Settings/SettingDialog.tsx (3 hunks)
  • components/Sidebar/Sidebar.tsx (2 hunks)
  • components/Spinner/Spinner.tsx (0 hunks)
  • contexts/ThemeContext.tsx (1 hunks)
  • pages/_app.tsx (2 hunks)
  • pages/_document.tsx (2 hunks)
  • pages/api/chat.ts (4 hunks)
  • pages/api/home/home.context.tsx (1 hunks)
  • pages/api/home/home.state.tsx (2 hunks)
  • pages/api/home/home.tsx (8 hunks)
  • pages/api/update-data-stream.ts (1 hunks)
  • pages/database-updates.tsx (1 hunks)
  • types/chat.ts (2 hunks)
  • utils/app/settings.ts (2 hunks)
💤 Files with no reviewable changes (7)
  • components/Markdown/Loading.tsx
  • components/Chat/Regenerate.tsx
  • components/Chatbar/components/Conversation.tsx
  • components/Chatbar/components/ClearConversations.tsx
  • components/Spinner/Spinner.tsx
  • components/Avatar/UserAvatar.tsx
  • components/Chatbar/components/ChatFolders.tsx
🧰 Additional context used
🧬 Code graph analysis (14)
contexts/ThemeContext.tsx (1)
utils/app/settings.ts (2)
  • getSettings (5-19)
  • saveSettings (21-23)
pages/_app.tsx (1)
contexts/ThemeContext.tsx (1)
  • ThemeProvider (19-61)
components/Chatbar/Chatbar.context.tsx (1)
types/chat.ts (1)
  • Conversation (21-28)
components/Markdown/MemoizedReactMarkdown.tsx (1)
__mocks__/react-markdown.js (1)
  • ReactMarkdown (7-9)
pages/api/chat.ts (1)
types/chat.ts (1)
  • ChatBody (14-19)
pages/database-updates.tsx (1)
contexts/ThemeContext.tsx (1)
  • useTheme (11-17)
components/Chat/ChatHeader.tsx (1)
contexts/ThemeContext.tsx (1)
  • useTheme (11-17)
pages/api/home/home.context.tsx (1)
types/chat.ts (1)
  • Conversation (21-28)
components/Chat/Chat.tsx (2)
types/websocket.ts (1)
  • WebSocketInbound (59-63)
types/chat.ts (1)
  • Message (1-10)
components/Chat/ChatMessage.tsx (1)
types/chat.ts (1)
  • Message (1-10)
pages/api/home/home.tsx (5)
pages/api/home/home.state.tsx (1)
  • initialState (34-82)
contexts/ThemeContext.tsx (1)
  • useTheme (11-17)
utils/app/conversation.ts (1)
  • saveConversation (26-38)
components/Chat/ChatHeader.tsx (1)
  • ChatHeader (20-262)
components/Chat/Chat.tsx (1)
  • Chat (137-1388)
components/Settings/SettingDialog.tsx (1)
contexts/ThemeContext.tsx (1)
  • useTheme (11-17)
components/Markdown/CustomComponents.tsx (5)
components/Markdown/CodeBlock.tsx (1)
  • CodeBlock (17-125)
components/Markdown/Image.tsx (1)
  • Image (78-78)
components/Markdown/Video.tsx (1)
  • Video (45-45)
components/Markdown/CustomDetails.tsx (1)
  • CustomDetails (8-141)
components/Markdown/CustomSummary.tsx (1)
  • CustomSummary (15-82)
components/Chat/ChatInput.tsx (1)
types/chat.ts (1)
  • Message (1-10)
🔇 Additional comments (14)
pages/api/home/home.context.tsx (1)

14-21: Consistent unused-parameter marker

Renaming the function signatures in the context props to _… keeps the contract intact while satisfying the lint rule for unused parameters. Looks good.

components/Markdown/CodeBlock.tsx (1)

2-2: Unused import cleanup

Dropping the unused MouseEvent import resolves the lint warning without touching runtime behavior. ✅

components/Markdown/CustomDetails.tsx (1)

101-101: ClassName formatting tidy-up

Thanks for trimming the extra whitespace in the class list; keeps the markup lint-happy with no functional change.

components/Markdown/Video.tsx (1)

9-46: Memoized alias export

Wrapping the memoized implementation in VideoComponent and exporting Video as an alias maintains the public API while appeasing the linter about display names. All good.

README.md (1)

88-97: Documentation sync with new endpoints

The additions for /chat/ca-rag and the live data streaming section line up with the recent feature work—thanks for keeping the README in step.

types/chat.ts (1)

17-28: Type surface aligned with new streaming state

Adding conversationId and selectedStream to the chat types captures the new payload/state requirements while remaining optional—nice incremental update.

pages/_app.tsx (1)

7-29: ThemeProvider wrap

Slotting ThemeProvider around the app component keeps the query client setup unchanged and unlocks the new theme context globally. 👍

components/Markdown/MemoizedReactMarkdown.tsx (1)

4-11: Named memoized component

Introducing MemoizedReactMarkdownComponent with an explicit displayName addresses the lint noise while preserving the memo logic. Looks solid.

components/Avatar/SystemAvatar.tsx (1)

1-10: LGTM

Import cleanup looks good; no behavioral changes.

components/Chat/MemoizedChatMessage.tsx (1)

2-16: LGTM

Consolidating the lodash import resolves the lint warning without changing behavior.

components/Markdown/CustomSummary.tsx (1)

38-79: LGTM

Whitespace normalization and removal of the unused icon keep the component tidy.

pages/api/home/home.state.tsx (1)

30-82: LGTM

The new data stream fields are wired consistently with existing env-flag patterns.

Dockerfile (1)

21-37: LGTM

Propagating NEXT_PUBLIC build args via ENV unblocks linted references during yarn build.

components/Folder/Folder.tsx (1)

24-33: LGTM

Parameter naming tweak silences the unused-arg lint without affecting behavior.

Comment on lines +30 to +44
const interval = setInterval(async () => {
try {
// Get text for selected stream
const textRes = await fetch(`/api/update-data-stream?stream=${selectedStream}`);
if (textRes.ok) {
const textData = await textRes.json();
if (typeof textData.text === 'string') {
setText(textData.text);
}
}
} catch (err) {
// Optionally handle error
}
}, 100);
return () => clearInterval(interval);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Dial back the 100 ms polling cadence

We now hit /api/update-data-stream ten times per second for every open tab. That’s a hot loop that will saturate the route and the upstream data source under even modest load, especially once multiple users watch the page. Please stretch the interval (e.g., 1–2 s) or switch to a push mechanism such as SSE/websocket before merging.

-    }, 100);
+    }, 1000);
📝 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 interval = setInterval(async () => {
try {
// Get text for selected stream
const textRes = await fetch(`/api/update-data-stream?stream=${selectedStream}`);
if (textRes.ok) {
const textData = await textRes.json();
if (typeof textData.text === 'string') {
setText(textData.text);
}
}
} catch (err) {
// Optionally handle error
}
}, 100);
return () => clearInterval(interval);
const interval = setInterval(async () => {
try {
// Get text for selected stream
const textRes = await fetch(`/api/update-data-stream?stream=${selectedStream}`);
if (textRes.ok) {
const textData = await textRes.json();
if (typeof textData.text === 'string') {
setText(textData.text);
}
}
} catch (err) {
// Optionally handle error
}
}, 1000);
return () => clearInterval(interval);
🤖 Prompt for AI Agents
components/DataStreamDisplay/DataStreamDisplay.tsx around lines 30-44: the
component currently polls /api/update-data-stream every 100ms which will
overload the route under real use; change the polling interval to a much lower
frequency (e.g., 1000–2000 ms) or replace the polling with a server push
approach (SSE or websocket) — if staying with polling, update the setInterval
delay to 1000 or 2000 and ensure proper cleanup remains; if switching to
SSE/websocket, open a persistent connection on mount, listen for messages to
setText, and close the connection on unmount.

Comment on lines +21 to +22
const streamTexts: { [streamId: string]: TextData } = {};
const finalizedEntries: FinalizedDataEntry[] = [];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid module-scoped state in API handlers

streamTexts and finalizedEntries live at module scope, so they reset whenever the function spins up on a new worker/lambda (which happens routinely in Next.js production) or when multiple instances serve requests in parallel. As soon as that happens the live/finalized data disappears, making the endpoint unreliable outside a single-process dev server. Please move this state into durable storage (database/Redis/etc.) or another shared backend so the data survives restarts and is consistent across instances.

🤖 Prompt for AI Agents
In pages/api/update-data-stream.ts around lines 21 to 22, the module-scoped
variables streamTexts and finalizedEntries are used to store request state,
which is unsafe because they are reset across process restarts and not shared
across instances; move this state into a durable/shared store (e.g., a database
or Redis) instead: replace reads/writes to streamTexts/finalizedEntries with
async calls to the chosen backend (get/update/append operations), initialize
connections outside hot paths, handle errors and retries, and ensure atomic
operations or transactions where concurrent requests may conflict so data
persists and remains consistent across workers.

Comment on lines +64 to +70
setEntries(data.entries || []);

// Extract unique stream IDs
const streamIds = data.entries.map((t: FinalizedDataEntry) => t.stream_id);
const streams = Array.from(new Set<string>(streamIds)).sort((a: string, b: string) => a.localeCompare(b));
setAvailableStreams(streams);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard against missing entries before mapping.

If /api/update-data-stream ever omits the entries array (e.g., on an empty response or transient backend glitch), data.entries.map(...) throws at runtime even though you just defaulted state to []. Please reuse the safeguarded array for both state and the stream-id extraction.

-      setEntries(data.entries || []);
-
-      // Extract unique stream IDs
-      const streamIds = data.entries.map((t: FinalizedDataEntry) => t.stream_id);
+      const fetchedEntries = (data.entries ?? []) as FinalizedDataEntry[];
+      setEntries(fetchedEntries);
+
+      // Extract unique stream IDs
+      const streamIds = fetchedEntries.map((t) => t.stream_id);
📝 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
setEntries(data.entries || []);
// Extract unique stream IDs
const streamIds = data.entries.map((t: FinalizedDataEntry) => t.stream_id);
const streams = Array.from(new Set<string>(streamIds)).sort((a: string, b: string) => a.localeCompare(b));
setAvailableStreams(streams);
const fetchedEntries = (data.entries ?? []) as FinalizedDataEntry[];
setEntries(fetchedEntries);
// Extract unique stream IDs
const streamIds = fetchedEntries.map((t) => t.stream_id);
const streams = Array.from(new Set<string>(streamIds))
.sort((a: string, b: string) => a.localeCompare(b));
setAvailableStreams(streams);
🤖 Prompt for AI Agents
In pages/database-updates.tsx around lines 64 to 70, the code uses
data.entries.map(...) which will throw if data.entries is undefined; instead
create a safeguarded variable (e.g., const entries = data.entries || []), call
setEntries(entries), then derive streamIds from entries (const streamIds =
entries.map(...)), and use that for creating the unique sorted streams before
calling setAvailableStreams; ensure types match (FinalizedDataEntry[]) when
declaring the safeguarded array.

@ericevans-nv ericevans-nv self-assigned this Sep 29, 2025
Copy link

@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: 5

Caution

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

⚠️ Outside diff range comments (2)
components/Avatar/SystemAvatar.tsx (1)

7-7: Dynamic Tailwind class names won't be included in the CSS bundle.

The template literals w-${width} and h-${height} construct class names at runtime, but Tailwind's static analysis won't detect these dynamic patterns. These classes will be missing from the final CSS, breaking the intended sizing behavior.

Apply this diff to use inline styles or a fixed set of Tailwind classes:

Solution 1 (Recommended): Use inline styles for dynamic sizing

-      className={`w-${width} h-${height} flex justify-center items-center rounded-full bg-[#004D3C] text-white`}
+      className="flex justify-center items-center rounded-full bg-[#004D3C] text-white"
+      style={{ width: `${width * 4}px`, height: `${height * 4}px` }}

Solution 2: Use a whitelist of allowed sizes with conditional rendering

+const sizeClasses = {
+  7: 'w-7 h-7',
+  8: 'w-8 h-8',
+  // add other sizes as needed
+};
+
 export const SystemAgentAvatar = ({ height = 7, width = 7 }) => {
+  const sizeClass = height === width ? sizeClasses[height] || 'w-7 h-7' : 'w-7 h-7';
   return (
     <div
-      className={`w-${width} h-${height} flex justify-center items-center rounded-full bg-[#004D3C] text-white`}
+      className={`${sizeClass} flex justify-center items-center rounded-full bg-[#004D3C] text-white`}
pages/api/home/home.tsx (1)

39-48: workflow should be stateful; otherwise title/description won’t update after mount

Mutating a local let in an effect doesn’t re-render. Use state and set it in the effect.

Apply:

-  let workflow = APPLICATION_NAME;
+  const [workflow, setWorkflow] = useState(APPLICATION_NAME);

And in the effect:

-    workflow = getWorkflowName();
+    setWorkflow(getWorkflowName());

Update deps:

-  }, [dispatch, t, setLightMode]);
+  }, [dispatch, t, setLightMode, setWorkflow]);

Additionally, add useState import:

- import { useEffect, useRef } from 'react';
+ import { useEffect, useRef, useState } from 'react';
♻️ Duplicate comments (2)
pages/api/update-data-stream.ts (1)

21-22: Module-scoped state makes the API non-durable across instances/restarts

This was already flagged in a previous review. Move streamTexts/finalizedEntries to durable/shared storage (DB/Redis/etc.) to work in serverless/multi-instance deployments.

pages/api/home/home.tsx (1)

11-13: DataStreamManager still mutates context state via in-place sort

The imported DataStreamManager uses currentStreams.sort() and newStreams.sort(), which mutates context state outside the reducer. Clone before sorting and dispatch a cloned array.

Change in components/DataStreamDisplay/DataStreamManager.tsx:

-            const currentStreams = dataStreams || [];
-            const newStreams = streamsData.streams;
-            if (JSON.stringify(currentStreams.sort()) !== JSON.stringify(newStreams.sort())) {
-              dispatch({ field: 'dataStreams', value: newStreams });
-            }
+            const currentStreamsSorted = [...(dataStreams ?? [])].sort();
+            const nextStreamsSorted = [...streamsData.streams].sort();
+            if (JSON.stringify(currentStreamsSorted) !== JSON.stringify(nextStreamsSorted)) {
+              dispatch({ field: 'dataStreams', value: nextStreamsSorted });
+            }
🧹 Nitpick comments (14)
components/Settings/Import.tsx (2)

10-10: Misleading parameter name change.

Prefixing the callback parameter with _ conventionally signals it's unused, but implementers of the onImport callback will use this parameter. The underscore is misleading in a type definition where the parameter name serves as documentation.

Consider either:

  1. Reverting to data and configuring ESLint to ignore parameter names in type definitions (e.g., via @typescript-eslint/no-unused-vars with args: 'none' for type definitions)
  2. Keeping a descriptive name without the underscore prefix

Apply this diff to revert the parameter name:

-  onImport: (_data: SupportedExportFormats) => void;
+  onImport: (data: SupportedExportFormats) => void;

Then configure your ESLint rule to not flag type definition parameters as unused.


28-31: Consider adding error handling for JSON parsing.

While beyond the scope of linter fixes, JSON.parse can throw if the file contains invalid JSON. Consider wrapping in try-catch to gracefully handle malformed files and provide user feedback.

Example enhancement:

 reader.onload = (e) => {
-  const json = JSON.parse(e.target?.result as string);
-  onImport(json);
+  try {
+    const json = JSON.parse(e.target?.result as string);
+    onImport(json);
+  } catch (error) {
+    // Handle error - e.g., show toast notification
+    console.error('Failed to parse import file:', error);
+  }
 };
components/Chat/ChatMessage.tsx (1)

269-269: Consider improving type safety by avoiding as any cast.

The rehypePlugins prop uses an as any type cast (also at lines 302, 327). This bypasses TypeScript's type checking and could mask type errors.

If the rehype plugin types are incompatible, consider:

  1. Installing correct type definitions for rehype-raw
  2. Creating a properly typed wrapper for the plugins
  3. Using a more specific type assertion if the actual types are known

Example of a more specific approach:

import { Plugin } from 'unified';

// Then use:
rehypePlugins={[rehypeRaw as Plugin]}

Or investigate if @types/rehype-raw provides the correct types that align with ReactMarkdown's expected plugin types.

components/Markdown/CustomComponents.tsx (2)

11-194: Consider if deep equality checks are necessary for all components.

All extracted components now use isEqual from lodash for memo comparison. While this provides thorough equality checks, it may be over-optimization for components with simple props:

  • Simple cases: Components like ThComponent, TdComponent, LiComponent receive only children prop. React's default shallow comparison or a simple prevProps.children === nextProps.children would suffice.
  • Complex cases: SupComponent with array filtering and CodeComponent with multiple props benefit from isEqual.

For simpler components, consider using React's default memo comparison or explicit checks:

-const ThComponent = memo(
-  ({ children }) => (
-    <th className="break-words border border-black bg-gray-500 px-3 py-1 text-white dark:border-white">
-      {children}
-    </th>
-  ),
-  (prevProps, nextProps) =>
-    isEqual(prevProps.children, nextProps.children),
-);
+const ThComponent = memo(({ children }) => (
+  <th className="break-words border border-black bg-gray-500 px-3 py-1 text-white dark:border-white">
+    {children}
+  </th>
+));

This reduces dependency on lodash and improves performance by avoiding deep equality checks where unnecessary.


2-2: Use modular imports for lodash utilities

Lodash is declared in package.json, but import { isEqual } from 'lodash' pulls in the entire library. Change to

import isEqual from 'lodash/isEqual'

or migrate to lodash-es for effective tree-shaking and a smaller bundle.

components/Chat/DataStreamControls.tsx (1)

44-51: Harden window.open against reverse tabnabbing

Add noopener/noreferrer features when opening a new tab.

-              onClick={() => window.open('/database-updates', '_blank')}
+              onClick={() =>
+                window.open('/database-updates', '_blank', 'noopener,noreferrer')
+              }
pages/api/chat.ts (2)

44-46: Prevent unbounded growth of initializedConversations

Module-scoped Set never clears; long-running edge workers can grow without bound. Add TTL/cleanup or limit entries, or key by a short-lived conversation/session token.

Also applies to: 71-88


77-78: Remove noisy console log in production path

Use debug-level logging or guard with an env flag.

pages/api/update-data-stream.ts (2)

39-41: Replace deprecated substr with slice

substr is deprecated and triggers linters.

-        id: `${streamId}-${currentTimestamp}-${Math.random().toString(36).substr(2, 9)}`,
+        id: `${streamId}-${currentTimestamp}-${Math.random().toString(36).slice(2, 11)}`,

111-133: Validate pending as boolean in PATCH

Currently accepts any value; add a type check.

   if (req.method === 'PATCH') {
     const { uuid, pending } = req.body;

     if (!uuid) {
       return res.status(400).json({ error: 'UUID is required.' });
     }
+    if (typeof pending !== 'boolean') {
+      return res.status(400).json({ error: 'pending must be a boolean.' });
+    }

     // Find the entry by UUID and update its pending status
components/Chat/Chat.tsx (4)

411-421: Add noopener/noreferrer to OAuth popup

Prevents reverse tabnabbing and drops opener reference.

-        const popup = window.open(
-          oauthUrl,
-          'oauth-popup',
-          'width=600,height=700,scrollbars=yes,resizable=yes'
-        );
+        const popup = window.open(
+          oauthUrl,
+          'oauth-popup',
+          'width=600,height=700,scrollbars=yes,resizable=yes,noopener,noreferrer'
+        );

887-1164: Remove constant condition and unreachable branch

if (!false) is a constant truth; the else never runs and will trip linters. Either always stream, or branch on content-type.

-          if (!false) {
+          // Prefer streaming path when body is a ReadableStream (default in Fetch)
+          if (response.body) {
             // streaming handling ...
-          } else {
-            const { answer } = await response?.json();
-            const updatedMessages: Message[] = [
-              ...updatedConversation.messages,
-              { role: 'assistant', content: answer },
-            ];
-            // ... state updates ...
-            homeDispatch({ field: 'loading', value: false });
-            homeDispatch({ field: 'messageIsStreaming', value: false });
-          }
+          }

Optionally branch on content-type:

  • text/event-stream or application/x-ndjson → stream
  • else → await response.json()

905-907: Remove unused counter and ESLint suppression

counter isn’t used; drop the variable and increment.

-            let counter = 1;
+            // removed unused counter

-              // eslint-disable-next-line no-unused-vars
-              counter++;
+              // (removed)

Also applies to: 974-976


311-315: Drop unused wsUrlObj/isCrossOrigin and ESLint disables

These vars aren’t used; remove to satisfy linter.

-      // eslint-disable-next-line no-unused-vars
-      const wsUrlObj = new URL(wsUrl);
-      // eslint-disable-next-line no-unused-vars
-      const isCrossOrigin = wsUrlObj.origin !== window.location.origin;
+      // (removed unused URL parsing; add back when needed)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f4d904 and d440b02.

📒 Files selected for processing (42)
  • components/Avatar/SystemAvatar.tsx (1 hunks)
  • components/Avatar/UserAvatar.tsx (0 hunks)
  • components/Chat/Chat.tsx (11 hunks)
  • components/Chat/ChatHeader.tsx (5 hunks)
  • components/Chat/ChatInput.tsx (9 hunks)
  • components/Chat/ChatInteractionMessage.tsx (1 hunks)
  • components/Chat/ChatMessage.tsx (2 hunks)
  • components/Chat/DataStreamControls.tsx (1 hunks)
  • components/Chat/MemoizedChatMessage.tsx (1 hunks)
  • components/Chat/Regenerate.tsx (0 hunks)
  • components/Chatbar/Chatbar.context.tsx (1 hunks)
  • components/Chatbar/Chatbar.tsx (1 hunks)
  • components/Chatbar/components/ChatFolders.tsx (0 hunks)
  • components/Chatbar/components/ChatbarSettings.tsx (1 hunks)
  • components/Chatbar/components/ClearConversations.tsx (0 hunks)
  • components/Chatbar/components/Conversation.tsx (0 hunks)
  • components/DataStreamDisplay/DataStreamDisplay.tsx (1 hunks)
  • components/DataStreamDisplay/DataStreamManager.tsx (1 hunks)
  • components/Folder/Folder.tsx (1 hunks)
  • components/Markdown/Chart.tsx (1 hunks)
  • components/Markdown/CodeBlock.tsx (1 hunks)
  • components/Markdown/CustomComponents.tsx (2 hunks)
  • components/Markdown/CustomDetails.tsx (1 hunks)
  • components/Markdown/CustomSummary.tsx (1 hunks)
  • components/Markdown/Image.tsx (2 hunks)
  • components/Markdown/Loading.tsx (0 hunks)
  • components/Markdown/MemoizedReactMarkdown.tsx (1 hunks)
  • components/Markdown/Video.tsx (2 hunks)
  • components/Search/Search.tsx (1 hunks)
  • components/Settings/Import.tsx (2 hunks)
  • components/Settings/SettingDialog.tsx (3 hunks)
  • components/Sidebar/Sidebar.tsx (2 hunks)
  • components/Spinner/Spinner.tsx (0 hunks)
  • pages/_app.tsx (2 hunks)
  • pages/_document.tsx (2 hunks)
  • pages/api/chat.ts (5 hunks)
  • pages/api/home/home.context.tsx (1 hunks)
  • pages/api/home/home.state.tsx (2 hunks)
  • pages/api/home/home.tsx (7 hunks)
  • pages/api/update-data-stream.ts (1 hunks)
  • pages/database-updates.tsx (1 hunks)
  • types/chat.ts (1 hunks)
💤 Files with no reviewable changes (7)
  • components/Spinner/Spinner.tsx
  • components/Avatar/UserAvatar.tsx
  • components/Chatbar/components/ClearConversations.tsx
  • components/Chatbar/components/ChatFolders.tsx
  • components/Chatbar/components/Conversation.tsx
  • components/Markdown/Loading.tsx
  • components/Chat/Regenerate.tsx
🚧 Files skipped from review as they are similar to previous changes (19)
  • types/chat.ts
  • components/Sidebar/Sidebar.tsx
  • components/Chat/ChatHeader.tsx
  • pages/api/home/home.context.tsx
  • pages/_app.tsx
  • components/Settings/SettingDialog.tsx
  • components/Markdown/Video.tsx
  • components/Markdown/CustomSummary.tsx
  • components/Chat/MemoizedChatMessage.tsx
  • pages/database-updates.tsx
  • components/Chatbar/Chatbar.tsx
  • components/DataStreamDisplay/DataStreamDisplay.tsx
  • components/Chatbar/components/ChatbarSettings.tsx
  • pages/_document.tsx
  • components/Search/Search.tsx
  • pages/api/home/home.state.tsx
  • components/Chatbar/Chatbar.context.tsx
  • components/Chat/ChatInteractionMessage.tsx
  • components/Chat/ChatInput.tsx
🧰 Additional context used
🧬 Code graph analysis (6)
pages/api/home/home.tsx (4)
pages/api/home/home.state.tsx (1)
  • initialState (34-82)
contexts/ThemeContext.tsx (1)
  • useTheme (11-17)
components/Chat/ChatHeader.tsx (1)
  • ChatHeader (21-227)
components/DataStreamDisplay/DataStreamManager.tsx (1)
  • DataStreamManager (16-70)
components/Markdown/CustomComponents.tsx (5)
components/Markdown/CodeBlock.tsx (1)
  • CodeBlock (17-125)
components/Markdown/Image.tsx (1)
  • Image (78-78)
components/Markdown/Video.tsx (1)
  • Video (45-45)
components/Markdown/CustomDetails.tsx (1)
  • CustomDetails (8-141)
components/Markdown/CustomSummary.tsx (1)
  • CustomSummary (15-82)
components/Chat/Chat.tsx (2)
types/websocket.ts (1)
  • WebSocketInbound (59-63)
types/chat.ts (1)
  • Message (1-10)
components/DataStreamDisplay/DataStreamManager.tsx (3)
types/chat.ts (1)
  • Conversation (20-27)
utils/app/conversation.ts (1)
  • saveConversation (26-38)
components/DataStreamDisplay/DataStreamDisplay.tsx (1)
  • DataStreamDisplay (170-170)
components/Chat/ChatMessage.tsx (1)
types/chat.ts (1)
  • Message (1-10)
components/Markdown/MemoizedReactMarkdown.tsx (1)
__mocks__/react-markdown.js (1)
  • ReactMarkdown (7-9)
🔇 Additional comments (14)
components/Avatar/SystemAvatar.tsx (1)

1-1: LGTM! Unused import removed.

The removal of the unused IconUserPentagon import correctly addresses the linter error.

components/Settings/Import.tsx (1)

29-29: LGTM!

Changing let to const is correct since the variable is never reassigned. This properly addresses the prefer-const linter rule.

components/Chat/ChatMessage.tsx (3)

15-17: LGTM! Markdown plugin imports are correct.

The imports for rehype-raw, remark-gfm, and remark-math are properly added and used throughout the component for enhanced Markdown rendering capabilities.


34-34: Verify: Underscore-prefixed parameters in callback interface seem unusual.

The underscore prefix on _editedMessage and _deleteCount in the Props interface definition is unconventional. Typically, underscore prefixes indicate intentionally unused parameters in an implementation, not in an interface definition. These parameters ARE used when onEdit is invoked at line 103:

onEdit({ ...message, content: messageContent }, deleteCount);

The decision to use/ignore these parameters should be made by the component implementing the callback, not declared in the interface. However, if this is a project-wide linting convention to fix linter errors (as indicated by the PR description), please confirm this is intentional.

Consider verifying if this naming pattern is required by your linter configuration or if the linter rule should be adjusted to not flag callback parameter names in interface definitions.


59-77: LGTM! useEffect hooks correctly placed before conditional returns.

All three useEffect hooks are properly positioned before the conditional return at line 81, which is essential for React's Rules of Hooks:

  1. Lines 60-62: Correctly syncs message.content to local state with proper dependency
  2. Lines 64-69: Properly auto-resizes textarea when isEditing changes
  3. Lines 71-77: Appropriately cleans up speech synthesis on unmount
components/Markdown/CodeBlock.tsx (1)

2-2: LGTM! Clean import removal.

The MouseEvent type was unused and has been correctly removed.

components/Markdown/CustomDetails.tsx (1)

101-101: LGTM! Formatting cleanup.

Trailing whitespace removed from className attribute.

components/Markdown/Chart.tsx (1)

3-3: LGTM! Import cleanup.

Simplified React import is appropriate for this component's usage.

components/Markdown/Image.tsx (2)

1-1: LGTM! Removed unused icon imports.

IconMaximize and IconX were not referenced in the component.


6-78: LGTM! Component aliasing pattern applied.

The refactoring introduces an internal ImageComponent constant with proper displayName and exports it as Image. This pattern:

  • Preserves the public API
  • Improves debugging with explicit displayName
  • Aligns with similar refactorings across the codebase (MemoizedReactMarkdown, CustomComponents)
components/Markdown/CustomComponents.tsx (1)

11-213: LGTM! Excellent refactoring for maintainability.

Extracting inline memo definitions into standalone named components significantly improves:

  • Code organization and readability
  • Debuggability with explicit displayName properties
  • Testability (each component can be tested independently)
  • Consistency across the Markdown rendering ecosystem

The refactoring maintains the same public API while improving the internal structure.

components/Markdown/MemoizedReactMarkdown.tsx (1)

4-10: LGTM! Consistent component aliasing pattern.

The refactoring matches the pattern applied in Image.tsx and aligns with the broader codebase modernization. The internal MemoizedReactMarkdownComponent with explicit displayName improves debugging while the public MemoizedReactMarkdown export maintains API compatibility.

pages/api/home/home.tsx (2)

53-53: Verify ThemeProvider wrapping and dark mode strategy

useTheme throws if no ThemeProvider. Ensure this page is wrapped. Also confirm Tailwind dark mode is applied via a parent with class "dark" (your container appends light/dark classes).


39-39: Confirm file location: pages/api is reserved for API routes

This file exports a React component and getServerSideProps but lives under pages/api/home/. Next.js treats pages/api/* as API routes. Verify routing is intentional; otherwise move to pages/home/ or app/… to avoid build/route conflicts.

Comment on lines +17 to +40
<div className="flex items-center gap-2 whitespace-nowrap">
<label className="flex items-center gap-2 cursor-pointer flex-shrink-0">
<span className="text-sm font-medium text-black dark:text-white">
Data Stream Display
</span>
<div
onClick={() => {
homeDispatch({
field: 'showDataStreamDisplay',
value: !showDataStreamDisplay,
});
}}
className={`relative inline-flex h-5 w-10 items-center cursor-pointer rounded-full transition-colors duration-300 ease-in-out ${
showDataStreamDisplay ? 'bg-black dark:bg-[#76b900]' : 'bg-gray-200'
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform duration-300 ease-in-out ${
showDataStreamDisplay ? 'translate-x-6' : 'translate-x-0'
}`}
/>
</div>
</label>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Make the toggle accessible (button/role="switch") instead of clickable div/label

Current markup isn’t keyboard-accessible and misuses label without a form control. Use a button with role="switch" and aria-checked.

Apply:

-        <label className="flex items-center gap-2 cursor-pointer flex-shrink-0">
+        <div className="flex items-center gap-2 flex-shrink-0">
           <span className="text-sm font-medium text-black dark:text-white">
           Data Stream Display
           </span>
-          <div
-            onClick={() => {
+          <button
+            type="button"
+            role="switch"
+            aria-checked={showDataStreamDisplay}
+            aria-label="Toggle Data Stream Display"
+            onClick={() => {
               homeDispatch({
                 field: 'showDataStreamDisplay',
                 value: !showDataStreamDisplay,
               });
             }}
-            className={`relative inline-flex h-5 w-10 items-center cursor-pointer rounded-full transition-colors duration-300 ease-in-out ${
+            className={`relative inline-flex h-5 w-10 items-center rounded-full transition-colors duration-300 ease-in-out ${
               showDataStreamDisplay ? 'bg-black dark:bg-[#76b900]' : 'bg-gray-200'
             }`}
           >
             <span
               className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform duration-300 ease-in-out ${
                 showDataStreamDisplay ? 'translate-x-6' : 'translate-x-0'
               }`}
             />
-          </div>
-        </label>
+          </button>
+        </div>
🤖 Prompt for AI Agents
In components/Chat/DataStreamControls.tsx around lines 17 to 40, the toggle is
implemented as a clickable div/label which is not keyboard-accessible and
misuses label; replace it with a semantic button element using role="switch" and
aria-checked={showDataStreamDisplay}, move the onClick handler to the button,
ensure the button has an accessible label (either visible text or
aria-label/aria-labelledby), keep the visual styling and transition classes on
the button, and remove the improper label wrapper so keyboard and assistive tech
can toggle state correctly (no extra key handlers needed since button handles
Enter/Space).

Comment on lines +45 to +49
const currentStreams = dataStreams || [];
const newStreams = streamsData.streams;
if (JSON.stringify(currentStreams.sort()) !== JSON.stringify(newStreams.sort())) {
dispatch({ field: 'dataStreams', value: newStreams });
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bug: in-place sort mutates state array

currentStreams.sort() mutates the array (dataStreams), causing unintended state changes. Sort copies before comparing.

-            const currentStreams = dataStreams || [];
-            const newStreams = streamsData.streams;
-            if (JSON.stringify(currentStreams.sort()) !== JSON.stringify(newStreams.sort())) {
+            const currentStreams = (dataStreams || []).slice().sort();
+            const newStreams = (streamsData.streams || []).slice().sort();
+            if (JSON.stringify(currentStreams) !== JSON.stringify(newStreams)) {
               dispatch({ field: 'dataStreams', value: newStreams });
             }
📝 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 currentStreams = dataStreams || [];
const newStreams = streamsData.streams;
if (JSON.stringify(currentStreams.sort()) !== JSON.stringify(newStreams.sort())) {
dispatch({ field: 'dataStreams', value: newStreams });
}
const currentStreams = (dataStreams || []).slice().sort();
const newStreams = (streamsData.streams || []).slice().sort();
if (JSON.stringify(currentStreams) !== JSON.stringify(newStreams)) {
dispatch({ field: 'dataStreams', value: newStreams });
}
🤖 Prompt for AI Agents
In components/DataStreamDisplay/DataStreamManager.tsx around lines 45 to 49, the
code calls currentStreams.sort() which mutates the state array (dataStreams);
fix this by cloning both arrays before sorting (e.g., use [...currentStreams]
and [...newStreams] or .slice()) and then compare the sorted clones with
JSON.stringify to avoid in-place mutation; ensure you sort the cloned arrays
consistently (use a comparator if items are not primitive strings) and only
dispatch when the serialized clones differ.

export const getReactMarkDownCustomComponents = (
messageIndex = 0,
messageId = '',
_messageId = '',
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Consider removing the unused parameter entirely.

The messageId parameter has been renamed to _messageId to indicate it's unused. If this parameter is truly unused throughout the codebase, consider removing it entirely to simplify the API.

Run this script to verify if any consumers pass this parameter:

If no consumers use the second parameter, apply this diff:

 export const getReactMarkDownCustomComponents = (
   messageIndex = 0,
-  _messageId = '',
 ) => {

🏁 Script executed:

#!/bin/bash
# Search for calls to getReactMarkDownCustomComponents with more than 1 argument
echo "=== Finding calls to getReactMarkDownCustomComponents ==="
rg -nP "getReactMarkDownCustomComponents\s*\([^)]*,[^)]*\)" --type ts --type tsx -C2

Length of output: 85


🏁 Script executed:

#!/bin/bash
echo "=== Finding calls to getReactMarkDownCustomComponents with >1 argument ==="
rg -nP "getReactMarkDownCustomComponents\s*\([^)]*,\s*" -g "*.ts" -g "*.tsx" -C2

Length of output: 485


Handle the existing second-argument usage before removing _messageId
There’s a consumer in components/Chat/ChatMessage.tsx (line 56):

getReactMarkDownCustomComponents(messageIndex, message?.id)

Removing the _messageId parameter will break this call—update it to only pass messageIndex before simplifying the API.

🤖 Prompt for AI Agents
In components/Markdown/CustomComponents.tsx around line 198, the function still
declares a second parameter `_messageId = ''` but callers (e.g.,
components/Chat/ChatMessage.tsx line 56) currently pass messageIndex and
message?.id; before removing `_messageId` update those call sites to stop
passing the second arg (change getReactMarkDownCustomComponents(messageIndex,
message?.id) to getReactMarkDownCustomComponents(messageIndex)) and then safely
remove the unused `_messageId` parameter and its default; ensure TypeScript
signatures and any tests/types are adjusted accordingly.

Comment on lines +58 to +59
const initUrl = chatCompletionURL.replace(/\/chat\/ca-rag$/, '/init');

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix brittle path rewrites; preserve query and handle trailing variants

Regex replace anchored to end-of-string misses URLs with query strings or trailing slashes. Use URL API to modify pathname safely.

-      const initUrl = chatCompletionURL.replace(/\/chat\/ca-rag$/, '/init');
+      const initUrl = (() => {
+        try {
+          const u = new URL(chatCompletionURL);
+          u.pathname = u.pathname.replace(/\/chat\/ca-rag\/?$/, '/init');
+          return u.toString();
+        } catch {
+          return chatCompletionURL.replace(/\/chat\/ca-rag(?:\/)?(?:\?.*)?$/, '/init');
+        }
+      })();
-  let backendURL = chatCompletionURL;
-  if (chatCompletionURL.includes(chatCaRagEndpoint)) {
-    // Replace '/chat/ca-rag' with '/call' to get the backend URL. The Context Aware RAG backend
-    // uses '/call' as its endpoint, which is too generic to use as the frontend endpoint here.
-    backendURL = chatCompletionURL.replace(/\/chat\/ca-rag$/, '/call');
-  }
+  let backendURL = chatCompletionURL;
+  try {
+    const u = new URL(chatCompletionURL);
+    if (u.pathname.endsWith('/chat/ca-rag')) {
+      u.pathname = u.pathname.replace(/\/chat\/ca-rag\/?$/, '/call');
+    }
+    backendURL = u.toString();
+  } catch {
+    backendURL = chatCompletionURL.replace(/\/chat\/ca-rag(?:\/)?(?:\?.*)?$/, '/call');
+  }

Also applies to: 325-331

🤖 Prompt for AI Agents
In pages/api/chat.ts around lines 58-59 (and similarly lines 325-331), the
current regex replace anchored to the end-of-string fails for URLs with query
strings or trailing slashes; parse chatCompletionURL with the URL API, preserve
search and hash, and mutate only the pathname: detect and strip a trailing
slash, replace a terminal '/chat/ca-rag' segment (or '/chat/ca-rag/' variant)
with '/init', then reconstruct the full URL via url.toString() so queries and
fragments remain intact.

Comment on lines +55 to +57
const webSocketModeRef = useRef(
typeof window !== 'undefined' && sessionStorage.getItem('webSocketMode') === 'false' ? false : webSocketMode
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Persisted WebSocket mode: 'true' value is ignored

The initializer only maps 'false' → false; 'true' falls back to default. Persisted 'true' won’t restore.

Apply:

-  const webSocketModeRef = useRef(
-    typeof window !== 'undefined' && sessionStorage.getItem('webSocketMode') === 'false' ? false : webSocketMode
-  );
+  const storedWsMode = typeof window !== 'undefined' ? sessionStorage.getItem('webSocketMode') : null;
+  const webSocketModeRef = useRef(
+    storedWsMode == null ? webSocketMode : storedWsMode === 'true'
+  );
📝 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 webSocketModeRef = useRef(
typeof window !== 'undefined' && sessionStorage.getItem('webSocketMode') === 'false' ? false : webSocketMode
);
const storedWsMode = typeof window !== 'undefined'
? sessionStorage.getItem('webSocketMode')
: null;
const webSocketModeRef = useRef(
storedWsMode == null ? webSocketMode : storedWsMode === 'true'
);
🤖 Prompt for AI Agents
In pages/api/home/home.tsx around lines 55-57, the useRef initializer only
converts a stored 'false' string to false and ignores a stored 'true' (falling
back to webSocketMode); update the initializer to explicitly handle both 'true'
and 'false' string values from sessionStorage (e.g., check
sessionStorage.getItem('webSocketMode') === 'true' → true, === 'false' → false,
otherwise fallback to webSocketMode) while preserving the typeof window guard.

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.

2 participants