Skip to content

Update Inline Style & Refactor code up to 500 LOC#158

Merged
Ayush8923 merged 6 commits into
feat/configuration-revampfrom
feat/update-inline-styles
May 11, 2026
Merged

Update Inline Style & Refactor code up to 500 LOC#158
Ayush8923 merged 6 commits into
feat/configuration-revampfrom
feat/update-inline-styles

Conversation

@Ayush8923
Copy link
Copy Markdown
Collaborator

@Ayush8923 Ayush8923 commented May 11, 2026

Summary

Two-part cleanup across feature pages and shared components:

  1. Inline-style migration — Replaced colors object references and inline style objects with Tailwind utilities + semantic CSS variables (bg-bg-secondary, text-text-primary,
    border-border, etc.) so styling stays consistent with the design tokens and themable from globals.css.
  2. 500-LOC cap enforcement — Split files that crossed the 500-LOC limit into focused sub-components and hooks (per CLAUDE.md guidance).

Changes

  1. Configurations / Prompt editor
  • Extracted save/rename logic from prompt-editor/page.tsx into a useConfigPersistence hook (~150 lines removed from the page).
  • Split ConfigEditorPane.tsx into LoadConfigDropdown, ConfigNameSection, and ToolsSection.
  • ConfigDiffPane.tsx: inline colors → semantic Tailwind classes; introduced a VersionPill component for consistent version display.
  1. Datasets
  • Split datasets/page.tsx into dedicated components under app/components/datasets/:
    • DatasetCard.tsx — individual card with metadata + delete action
    • DatasetListing.tsx — list display, pagination, empty/error states
    • DatasetListingSkeleton.tsx — loading skeleton
    • UploadDatasetModal.tsx — upload form (uses shared Field + Modal)
    • DeleteDatasetModal.tsx — delete confirmation (uses shared Modal)
  1. Speech-to-text
  • Extracted data fetching into a useSttData hook (~140 lines lifted out of the page).
  • Removed colors import in favor of Tailwind semantic tokens.
  1. Text-to-speech / Evaluations / ComingSoon / EvalDatasetDescription
  • Style-only: colors.* references and inline style objects → Tailwind semantic classes.
  • ComingSoon.tsx now uses the shared Button primitive instead of a bespoke button.

Summary by CodeRabbit

  • New Features

    • Modal-driven dataset upload & deletion, new dataset listing with pagination, and skeleton loading UI.
    • New prompt/configuration load, rename, save UX pieces and speech-to-text data hook.
  • Style

    • Replaced inline theme colors with Tailwind utility classes across many pages and components for consistent styling.
  • Refactor

    • Centralized config persistence and STT data logic; split prompt editor into smaller UI subcomponents.
  • Chore

    • Enforced max-lines ESLint rule (500 lines).

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 128b34e2-00f5-40c9-b04c-7d7460cc4efe

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9062d and 34cf3fa.

📒 Files selected for processing (2)
  • app/components/chat/ChatEmptyState.tsx
  • app/components/speech-to-text/WaveformVisualizer.tsx
✅ Files skipped from review due to trivial changes (1)
  • app/components/chat/ChatEmptyState.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/speech-to-text/WaveformVisualizer.tsx

📝 Walkthrough

Walkthrough

This PR migrates UI styling to Tailwind, extracts prompt-editor subcomponents and a persistence hook, centralizes STT data in a new hook, introduces dataset listing and modal components with modal-based upload/delete flows, updates waveform/canvas color resolution, and enables ESLint max-lines.

Changes

Tailwind Styling Migration

Layer / File(s) Summary
Colors Module Removal
app/lib/colors.ts
Deleted the exported colors constant containing centralized theme values for backgrounds, text, borders, accents, and status states.
Page Background & Layout
app/(main)/evaluations/page.tsx, app/(main)/speech-to-text/page.tsx, app/(main)/text-to-speech/page.tsx
Updated root page containers to use bg-bg-secondary Tailwind class and authentication-required panels to use class-based text/background styling instead of inline colors values.
Component Text & Interactive Styling
app/components/evaluations/EvalDatasetDescription.tsx, app/components/speech-to-text/DatasetDescription.tsx, app/components/text-to-speech/DatasetDescription.tsx
Replaced inline style={{ color: ... }} with Tailwind text color classes (text-text-primary, text-text-secondary) and added cursor-pointer to buttons.
Audio Player Progress & Controls
app/components/speech-to-text/AudioPlayer.tsx, app/components/speech-to-text/AudioPlayerFromUrl.tsx, app/components/text-to-speech/AudioPlayerFromUrl.tsx
Introduced progressPercent computed value and replaced inline color styling with Tailwind classes for button, progress bar, and text elements.
Diff Viewers & Complex UIs
app/components/prompt-editor/ConfigDiffPane.tsx, app/components/prompt-editor/PromptDiffPane.tsx, app/components/speech-to-text/TranscriptionDiffViewer.tsx
Refactored diff UI to use Tailwind highlight classes, introduced VersionPill components for version display, added DiffLineRow helper, and replaced inline background/text color logic.
Canvas-Based Visualization
app/components/speech-to-text/WaveformVisualizer.tsx
Added readThemeColor() helper to resolve theme colors from CSS variables at runtime; updated static and animated waveform drawing to use theme-resolved accent and border colors.
ComingSoon Component
app/components/ComingSoon.tsx
Replaced inline colors-based styling and raw <button> with Tailwind utility classes and shared Button component; removed global <style jsx> keyframes.

Prompt Editor Refactoring with Hooks & Components

Layer / File(s) Summary
Config Persistence Hook
app/hooks/useConfigPersistence.ts
New hook owns API interactions for creating/updating/renaming configurations; validates inputs, assembles configBlob with GPT-5 conditional parameter handling, posts/patches to config endpoints, shows success/error toasts, and invalidates cache.
Load Config Dropdown
app/components/prompt-editor/LoadConfigDropdown.tsx
New component renders grouped config version selector with lazy-loading support via loadVersionsForConfig and optional loadSingleVersion fetch; shows loading spinners and selection checkmarks.
Config Name Section
app/components/prompt-editor/ConfigNameSection.tsx
New component supports unbound mode (edit name locally) and bound mode (dedicated rename flow with async onRenameConfig callback); includes validation and conflict checking against allConfigMeta.
Tools Section
app/components/prompt-editor/ToolsSection.tsx
New component manages Tool array with knowledge base ID field and conditional max-results input (hidden for GPT-5); includes per-tool hover tooltip.
Config Editor Pane
app/components/prompt-editor/ConfigEditorPane.tsx
Refactored to use extracted subcomponents (LoadConfigDropdown, ConfigNameSection, ToolsSection) instead of inline implementation; simplified render tree and import list.
Prompt Editor Page
app/(main)/configurations/prompt-editor/page.tsx
Refactored to use useConfigPersistence hook for save/rename, reorganized URL-param initialization with cached version items, added version repopulation effect, and simplified handleSave/handleRename handlers.

Datasets Modal UI Refactoring

Layer / File(s) Summary
Dataset Card
app/components/datasets/DatasetCard.tsx
New component displays dataset metadata (name, ID, item counts, duplication factor) with delete button.
Dataset Listing Skeleton
app/components/datasets/DatasetListingSkeleton.tsx
New animated placeholder component for loading state with configurable count.
Dataset Listing
app/components/datasets/DatasetListing.tsx
New component renders dataset management UI with conditional states (loading/unauthenticated/error/empty/list) and pagination; maps datasets to DatasetCard rows and wires delete handler.
Upload Dataset Modal
app/components/datasets/UploadDatasetModal.tsx
New modal for CSV upload with file picker, conditional dataset metadata fields (shown after file selection), validation, and upload/cancel actions.
Delete Dataset Modal
app/components/datasets/DeleteDatasetModal.tsx
New modal for confirming dataset deletion with optional dataset name display and disabled buttons during deletion.
Datasets Page
app/(main)/datasets/page.tsx
Refactored to normalize API responses, implement modal-based upload/delete flows with reset helpers, and wire handlers into DatasetListing and modal components.

Speech-to-Text Data Management Hook

Layer / File(s) Summary
useSttData Hook
app/hooks/useSttData.ts
New hook centralizes languages, datasets, runs, results state and loader functions; gates loading on authentication, normalizes multiple API response shapes, applies language filtering, and maps results to STTResult objects with sample metadata and signed URLs.
Speech-to-Text Page
app/(main)/speech-to-text/page.tsx
Refactored to delegate STT data management to useSttData(activeTab) hook; removed local state initialization and updated styling from inline colors to Tailwind classes.

Linting Configuration

Layer / File(s) Summary
ESLint Max Lines
eslint.config.mjs
Enabled max-lines rule to error at 500 total lines per file while ignoring blank lines and comments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • nishika26
  • Prajna1999

Poem

🐰 I hopped through code in warm daylight,

Replaced old hues with Tailwind bright.
Hooks and modals, neatly spun,
Components split — the work is done.
A carrot-toast for builds gone right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the PR's primary objectives: migrating inline styles to Tailwind utilities and enforcing a 500-LOC limit via refactoring, both of which are prominently reflected across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/update-inline-styles

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.

@Ayush8923 Ayush8923 self-assigned this May 11, 2026
Copy link
Copy Markdown

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

Caution

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

⚠️ Outside diff range comments (2)
app/(main)/configurations/prompt-editor/page.tsx (1)

178-185: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against non-numeric/NaN urlVersion.

parseInt(urlVersion) has no radix and no NaN check. A URL with ?version=foo (or anything non-numeric) will hand NaN to loadSingleVersion, which is likely to result in a 404/500 or a confusing error. Validate and fall back to the latest version on bad input.

🛡️ Suggested guard
-      const versionNum = urlVersion
-        ? parseInt(urlVersion)
-        : items.reduce((a, b) => (b.version > a.version ? b : a)).version;
+      const parsed = urlVersion ? parseInt(urlVersion, 10) : NaN;
+      const latest = items.reduce((a, b) =>
+        b.version > a.version ? b : a,
+      ).version;
+      const versionNum = Number.isFinite(parsed) ? parsed : latest;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/configurations/prompt-editor/page.tsx around lines 178 - 185, The
code currently does parseInt(urlVersion) without a radix or NaN check which can
pass NaN into loadSingleVersion; change the logic around urlVersion/versionNum
to use parseInt(urlVersion, 10) and validate it with Number.isNaN (or
Number.isFinite) and if the parsed value is invalid or urlVersion is falsy, fall
back to computing the latest version from items (the existing items.reduce(...)
expression); keep using loadSingleVersion(urlConfigId, versionNum), then
applyConfig and setEditorReady as before.
app/(main)/datasets/page.tsx (1)

53-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Switch these client-side requests to clientFetch.

This page is a client component, so using apiFetch here skips the shared 401 refresh / AUTH_EXPIRED_EVENT handling the repo expects.

As per coding guidelines, "Use clientFetch(endpoint, options) on the client-side for API requests, which handles token refresh on 401 errors and dispatches AUTH_EXPIRED_EVENT when refresh fails".

Also applies to: 103-106, 135-139

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/datasets/page.tsx around lines 53 - 56, Replace client-side uses
of apiFetch with clientFetch so token refresh and AUTH_EXPIRED_EVENT handling
occur; specifically change the call at the top of page.tsx (the
apiFetch<Dataset[] | { data: Dataset[] }>( "/api/evaluations/datasets",
apiKey?.key ?? "" )) to use clientFetch with the same endpoint and pass the API
key via the options object (e.g., clientFetch("/api/evaluations/datasets", {
apiKey: apiKey?.key ?? "" })) and preserve the expected return type handling,
update the other occurrences mentioned (the calls around 103-106 and 135-139)
similarly, and update imports to import clientFetch (remove or stop using
apiFetch) so AUTH_EXPIRED_EVENT/token refresh behavior is used.
🧹 Nitpick comments (4)
app/hooks/useSttData.ts (1)

65-78: ⚡ Quick win

Consider extracting response normalization logic.

The nested conditionals handling multiple API response shapes are defensive but difficult to maintain. Consider extracting this pattern into a reusable helper function since similar logic appears in loadDatasets (lines 103-107), loadRuns (lines 126-129), and loadResults (lines 150-164).

♻️ Example refactoring approach
// In a shared utility file
function normalizeArrayResponse<T>(
  data: unknown,
  possibleKeys: string[]
): T[] {
  if (Array.isArray(data)) return data;
  
  for (const key of possibleKeys) {
    const value = (data as any)?.[key];
    if (Array.isArray(value)) return value;
    if (value?.data && Array.isArray(value.data)) return value.data;
  }
  
  return [];
}

// Usage in hook
const rawList = normalizeArrayResponse<RawLanguage>(
  data,
  ['languages', 'data']
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/hooks/useSttData.ts` around lines 65 - 78, Extract the repeated
response-normalization logic into a reusable helper (e.g.,
normalizeArrayResponse<T>(data: unknown, possibleKeys: string[]): T[]) placed in
a shared utilities module, then replace the conditional block that builds
rawList in useSttData.ts (the block assigning rawList) with a call to
normalizeArrayResponse<RawLanguage>(data, ['languages','data']); likewise update
the similar normalization usages in loadDatasets, loadRuns, and loadResults to
call this helper so all variants (array, {data: [...]} and nested
{data:{data:[...]}}) are handled consistently and return an empty array by
default.
app/hooks/useConfigPersistence.ts (1)

47-50: 💤 Low value

Move auth check before the name-validation toast.

A user not logged in gets "Please enter a configuration name" instead of "Please log in…" if their name is blank. Reordering the two guards (or combining them) gives more accurate feedback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/hooks/useConfigPersistence.ts` around lines 47 - 50, In
useConfigPersistence, the guard that shows the "Please enter a configuration
name" toast is currently executed before checking authentication; change the
order (or combine the checks) so isAuthenticated from useAuth is evaluated first
and, if false, show the "Please log in…" toast (before validating
name/activeKey). Update the logic around toast, activeKey and isAuthenticated
(and preserve setIsSaving/isSaving behavior) so unauthenticated users receive
the login message instead of the name-validation message.
app/(main)/configurations/prompt-editor/page.tsx (2)

230-238: 💤 Low value

Redundant effect dependencies.

provider, temperature, and tools are all subsumed by currentConfigBlob (which is already a dep) — the effect body never references those three standalone state values. They cause the diff effect to re-run on every keystroke that touches setProvider/setTemperature/setTools independently of currentConfigBlob. Trim the deps to what the effect actually reads.

   }, [
     selectedConfigId,
     currentContent,
     currentConfigBlob,
-    provider,
-    temperature,
-    tools,
     savedConfigs,
   ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/configurations/prompt-editor/page.tsx around lines 230 - 238, The
effect's dependency array includes provider, temperature, and tools even though
the effect body only reads selectedConfigId, currentContent, currentConfigBlob,
and savedConfigs; remove provider, temperature, and tools from the dependency
list in the effect (the array currently passed to useEffect) so it depends only
on the values actually referenced (selectedConfigId, currentContent,
currentConfigBlob, savedConfigs) and ensure any lint rule complaints are
addressed by keeping the actual referenced symbols in the array.

27-82: 🏗️ Heavy lift

Address the lint complexity warning by extracting URL-init/diff effects.

Static analysis flags PromptEditorContent at complexity 12 / 41 statements, exceeding the configured limits (10 / 20). The save/rename extraction in this PR is a solid step, but the URL-param initialization effect (lines 156-196) and the unsaved-changes diff effect (lines 206-238) are both self-contained and prime candidates to move into dedicated hooks (e.g. useUrlConfigInit, useUnsavedConfigChanges) to bring this component back under the project's lint budget.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/configurations/prompt-editor/page.tsx around lines 27 - 82,
PromptEditorContent is flagged for cyclomatic complexity; extract the URL-param
initialization effect and the unsaved-changes diff effect into two hooks to
reduce complexity. Create useUrlConfigInit that encapsulates the effect
currently using
searchParams/urlConfigId/urlVersion/urlDatasetId/urlExperimentName/fromEvaluations/isNewConfig
and calls back into PromptEditorContent via setters (e.g., setEditorReady,
setSelectedConfigId, setCurrentConfigBlob, setCurrentContent,
setCurrentConfigName, setCurrentConfigVersion, setCurrentConfigParentId) and
uses functions from the component (loadVersionsForConfig, loadSingleVersion)
plus version maps (hookVersionItemsMap, stableVersionItemsMap) to perform the
same initialization logic. Create useUnsavedConfigChanges that contains the
effect comparing currentConfigBlob/currentContent/selectedVersion/compareWith to
setHasUnsavedChanges and setCommitMessage, and expose any small helpers needed
by the component. Replace the two in-component effects with calls to
useUrlConfigInit(...) and useUnsavedConfigChanges(...), passing only the minimal
state and callbacks (also include editorInitialized and setEditorReady where
relevant) so PromptEditorContent retains behavior while staying under lint
complexity limits.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`(main)/datasets/page.tsx:
- Around line 153-155: When datasets change you must clamp currentPage so it
never exceeds totalPages: calculate totalPages = Math.ceil(datasets.length /
ITEMS_PER_PAGE) and, if currentPage > Math.max(1, totalPages), set currentPage =
Math.max(1, totalPages) before computing currentDatasets; implement this clamp
in the component (e.g., a useEffect watching datasets or right after computing
totalPages) so datasets.slice(start, start + ITEMS_PER_PAGE) always uses a valid
page.

In `@app/`(main)/speech-to-text/page.tsx:
- Around line 68-72: The effect that updates datasetLanguageId (useEffect) will
overwrite a user-selected language whenever languages changes; modify the effect
so it only sets setDatasetLanguageId(languages[0].id) when the current
datasetLanguageId is still the default/uninitialized value (e.g., 1 or null) to
avoid clobbering user selection, and include datasetLanguageId in the dependency
array so the guard uses the latest value; keep references to useEffect,
languages, datasetLanguageId, and setDatasetLanguageId when making the change.

In `@app/components/ComingSoon.tsx`:
- Line 60: The Button in the ComingSoon component currently calls router.back()
which can do nothing for direct-entry visits; update the onClick handler for the
Button (in ComingSoon) to attempt router.back() and fall back to a safe route
(e.g., router.push('/') or router.replace('/dashboard')) when history is
absent—detect with a check like window.history.length <= 1 or by awaiting
router.back() and then calling the fallback; ensure you reference the same
Button onClick and the router instance used in ComingSoon.

In `@app/components/datasets/DatasetListing.tsx`:
- Around line 35-42: The Upload CTA in DatasetListing (the Button that calls
onUploadNew with the PlusIcon) is still active when the component renders the
"Login required" state; update DatasetListing to check the auth state (e.g., the
user or isAuthenticated prop/state used to render the login message) and either
hide the Button entirely or render it disabled and no-op when auth is missing.
Ensure the conditional references the same auth variable used elsewhere in
DatasetListing so unauthenticated users cannot trigger onUploadNew.

In `@app/components/datasets/UploadDatasetModal.tsx`:
- Around line 32-33: The hidden native file input isn't being cleared when
resetting the form, so re-selecting the same CSV may not trigger onChange;
update the reset/close/success paths that currently clear selectedFile
(references: fileInputRef, selectedFile, datasetName, isUploading) to also clear
the native input by setting fileInputRef.current.value to an empty string (and
keep fileInputRef.current = null if you already null it), and ensure this is
done wherever selectedFile is reset (e.g., after successful upload and on modal
close) so the browser will fire onChange for the same file next time.
- Around line 36-42: The modal can still close via backdrop click or Escape even
when an upload is in flight; update UploadDatasetModal so Modal respects the
upload state: either pass Modal props to disable backdrop/Escape dismissal if
supported (e.g., disableBackdropClick/disableEscapeKeyDown or equivalent) or
guard the onClose handler so it becomes a no-op while uploadInFlight/isUploading
is true; ensure you reference the Modal usage and the onClose prop in
UploadDatasetModal and use the existing upload state
(uploadInFlight/isUploading) to prevent premature closing.

In `@app/components/prompt-editor/LoadConfigDropdown.tsx`:
- Around line 82-95: handleSelectVersion can silently do nothing when a
requested config isn't found and loadSingleVersion is absent or returns null;
update handleSelectVersion to handle that error path by showing user feedback
and closing the dropdown: if full is falsy and either loadSingleVersion is
undefined or returns null, call setIsOpen(false) and surface an error (e.g.,
trigger the existing toast/notification utility or call a provided onError
handler) before returning, otherwise proceed to call onLoadConfig(config) as
before; reference handleSelectVersion, savedConfigs, loadSingleVersion,
onLoadConfig, and setIsOpen when making the changes.

In `@app/components/prompt-editor/ToolsSection.tsx`:
- Around line 40-44: The list rendering in tools.map uses key={index}, which
breaks reconciliation when items are removed (see tools.map,
onRemoveTool(index), showTooltip and Field/input), so replace the positional key
with a stable per-tool identifier: add an id property to the Tool objects when
they are created (or generate a stable id in the code path that adds tools) and
use key={tool.id} in the map and for any related keyed elements like
showTooltip; ensure all usages that relied on index for identity (tooltip keys,
focused/controlled Field inputs) switch to tool.id so focus/IME/tooltip state
stays consistent after removals.
- Around line 87-98: The numeric input handling for tool.max_num_results is
fragile: stop using parseInt(e.target.value) || 20 because it forces 20 for
empty or "0" and lacks radix/constraints; instead, keep the raw input when
e.target.value === "" so the field can be temporarily empty, parse with
parseInt(value, 10) only when value !== "" and Number.isInteger(parsed) to
produce a sanitized integer, clamp with Math.max(0, parsed) if you want
non-negative results, and call onUpdateTool(index, "max_num_results",
sanitizedValue) (or null/undefined for empty) accordingly; also add input
attributes min={0} step={1} to the <input> to prevent negatives/decimals and
make typing predictable.

In `@app/hooks/useConfigPersistence.ts`:
- Around line 74-83: The aggregation of max_num_results is incorrect: the
forEach with the condition allKnowledgeBaseIds.length ===
tool.knowledge_base_ids.length only matches on the first pushed tool and is
unclear; instead explicitly pick the first tool's max_num_results (or clearly
document a different policy). Replace the loop logic around
currentConfigBlob.completion.params.tools so that you compute
allKnowledgeBaseIds by flattening tool.knowledge_base_ids (e.g., using flatMap
or reduce) and set maxNumResults = tools[0].max_num_results when tools.length >
0 (or if you intended "last tool wins", assign maxNumResults =
tool.max_num_results on each iteration); adjust references to tools,
allKnowledgeBaseIds, and maxNumResults accordingly.

---

Outside diff comments:
In `@app/`(main)/configurations/prompt-editor/page.tsx:
- Around line 178-185: The code currently does parseInt(urlVersion) without a
radix or NaN check which can pass NaN into loadSingleVersion; change the logic
around urlVersion/versionNum to use parseInt(urlVersion, 10) and validate it
with Number.isNaN (or Number.isFinite) and if the parsed value is invalid or
urlVersion is falsy, fall back to computing the latest version from items (the
existing items.reduce(...) expression); keep using
loadSingleVersion(urlConfigId, versionNum), then applyConfig and setEditorReady
as before.

In `@app/`(main)/datasets/page.tsx:
- Around line 53-56: Replace client-side uses of apiFetch with clientFetch so
token refresh and AUTH_EXPIRED_EVENT handling occur; specifically change the
call at the top of page.tsx (the apiFetch<Dataset[] | { data: Dataset[] }>(
"/api/evaluations/datasets", apiKey?.key ?? "" )) to use clientFetch with the
same endpoint and pass the API key via the options object (e.g.,
clientFetch("/api/evaluations/datasets", { apiKey: apiKey?.key ?? "" })) and
preserve the expected return type handling, update the other occurrences
mentioned (the calls around 103-106 and 135-139) similarly, and update imports
to import clientFetch (remove or stop using apiFetch) so
AUTH_EXPIRED_EVENT/token refresh behavior is used.

---

Nitpick comments:
In `@app/`(main)/configurations/prompt-editor/page.tsx:
- Around line 230-238: The effect's dependency array includes provider,
temperature, and tools even though the effect body only reads selectedConfigId,
currentContent, currentConfigBlob, and savedConfigs; remove provider,
temperature, and tools from the dependency list in the effect (the array
currently passed to useEffect) so it depends only on the values actually
referenced (selectedConfigId, currentContent, currentConfigBlob, savedConfigs)
and ensure any lint rule complaints are addressed by keeping the actual
referenced symbols in the array.
- Around line 27-82: PromptEditorContent is flagged for cyclomatic complexity;
extract the URL-param initialization effect and the unsaved-changes diff effect
into two hooks to reduce complexity. Create useUrlConfigInit that encapsulates
the effect currently using
searchParams/urlConfigId/urlVersion/urlDatasetId/urlExperimentName/fromEvaluations/isNewConfig
and calls back into PromptEditorContent via setters (e.g., setEditorReady,
setSelectedConfigId, setCurrentConfigBlob, setCurrentContent,
setCurrentConfigName, setCurrentConfigVersion, setCurrentConfigParentId) and
uses functions from the component (loadVersionsForConfig, loadSingleVersion)
plus version maps (hookVersionItemsMap, stableVersionItemsMap) to perform the
same initialization logic. Create useUnsavedConfigChanges that contains the
effect comparing currentConfigBlob/currentContent/selectedVersion/compareWith to
setHasUnsavedChanges and setCommitMessage, and expose any small helpers needed
by the component. Replace the two in-component effects with calls to
useUrlConfigInit(...) and useUnsavedConfigChanges(...), passing only the minimal
state and callbacks (also include editorInitialized and setEditorReady where
relevant) so PromptEditorContent retains behavior while staying under lint
complexity limits.

In `@app/hooks/useConfigPersistence.ts`:
- Around line 47-50: In useConfigPersistence, the guard that shows the "Please
enter a configuration name" toast is currently executed before checking
authentication; change the order (or combine the checks) so isAuthenticated from
useAuth is evaluated first and, if false, show the "Please log in…" toast
(before validating name/activeKey). Update the logic around toast, activeKey and
isAuthenticated (and preserve setIsSaving/isSaving behavior) so unauthenticated
users receive the login message instead of the name-validation message.

In `@app/hooks/useSttData.ts`:
- Around line 65-78: Extract the repeated response-normalization logic into a
reusable helper (e.g., normalizeArrayResponse<T>(data: unknown, possibleKeys:
string[]): T[]) placed in a shared utilities module, then replace the
conditional block that builds rawList in useSttData.ts (the block assigning
rawList) with a call to normalizeArrayResponse<RawLanguage>(data,
['languages','data']); likewise update the similar normalization usages in
loadDatasets, loadRuns, and loadResults to call this helper so all variants
(array, {data: [...]} and nested {data:{data:[...]}}) are handled consistently
and return an empty array by default.
🪄 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: CHILL

Plan: Pro

Run ID: 6c308d75-599e-410a-9015-babb5940b01a

📥 Commits

Reviewing files that changed from the base of the PR and between 06548fc and 5a9062d.

📒 Files selected for processing (29)
  • app/(main)/configurations/prompt-editor/page.tsx
  • app/(main)/datasets/page.tsx
  • app/(main)/evaluations/page.tsx
  • app/(main)/speech-to-text/page.tsx
  • app/(main)/text-to-speech/page.tsx
  • app/components/ComingSoon.tsx
  • app/components/datasets/DatasetCard.tsx
  • app/components/datasets/DatasetListing.tsx
  • app/components/datasets/DatasetListingSkeleton.tsx
  • app/components/datasets/DeleteDatasetModal.tsx
  • app/components/datasets/UploadDatasetModal.tsx
  • app/components/evaluations/EvalDatasetDescription.tsx
  • app/components/prompt-editor/ConfigDiffPane.tsx
  • app/components/prompt-editor/ConfigEditorPane.tsx
  • app/components/prompt-editor/ConfigNameSection.tsx
  • app/components/prompt-editor/LoadConfigDropdown.tsx
  • app/components/prompt-editor/PromptDiffPane.tsx
  • app/components/prompt-editor/ToolsSection.tsx
  • app/components/speech-to-text/AudioPlayer.tsx
  • app/components/speech-to-text/AudioPlayerFromUrl.tsx
  • app/components/speech-to-text/DatasetDescription.tsx
  • app/components/speech-to-text/TranscriptionDiffViewer.tsx
  • app/components/speech-to-text/WaveformVisualizer.tsx
  • app/components/text-to-speech/AudioPlayerFromUrl.tsx
  • app/components/text-to-speech/DatasetDescription.tsx
  • app/hooks/useConfigPersistence.ts
  • app/hooks/useSttData.ts
  • app/lib/colors.ts
  • eslint.config.mjs
💤 Files with no reviewable changes (1)
  • app/lib/colors.ts

Comment on lines +153 to +155
const totalPages = Math.ceil(datasets.length / ITEMS_PER_PAGE);
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const currentDatasets = datasets.slice(start, start + ITEMS_PER_PAGE);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp currentPage after deletes and refetches.

If the last item on the last page is removed, currentPage can point past totalPages. The next render slices an empty page and shows “No datasets found” even though earlier pages still contain data.

Proposed fix
   const totalPages = Math.ceil(datasets.length / ITEMS_PER_PAGE);
+  useEffect(() => {
+    if (totalPages === 0 && currentPage !== 1) {
+      setCurrentPage(1);
+      return;
+    }
+
+    if (totalPages > 0 && currentPage > totalPages) {
+      setCurrentPage(totalPages);
+    }
+  }, [currentPage, totalPages]);
+
   const start = (currentPage - 1) * ITEMS_PER_PAGE;
   const currentDatasets = datasets.slice(start, start + ITEMS_PER_PAGE);
📝 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 totalPages = Math.ceil(datasets.length / ITEMS_PER_PAGE);
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const currentDatasets = datasets.slice(start, start + ITEMS_PER_PAGE);
const totalPages = Math.ceil(datasets.length / ITEMS_PER_PAGE);
useEffect(() => {
if (totalPages === 0 && currentPage !== 1) {
setCurrentPage(1);
return;
}
if (totalPages > 0 && currentPage > totalPages) {
setCurrentPage(totalPages);
}
}, [currentPage, totalPages]);
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const currentDatasets = datasets.slice(start, start + ITEMS_PER_PAGE);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/datasets/page.tsx around lines 153 - 155, When datasets change
you must clamp currentPage so it never exceeds totalPages: calculate totalPages
= Math.ceil(datasets.length / ITEMS_PER_PAGE) and, if currentPage > Math.max(1,
totalPages), set currentPage = Math.max(1, totalPages) before computing
currentDatasets; implement this clamp in the component (e.g., a useEffect
watching datasets or right after computing totalPages) so datasets.slice(start,
start + ITEMS_PER_PAGE) always uses a valid page.

Comment on lines 68 to +72
useEffect(() => {
loadLanguages();
loadDatasets();
if (activeTab === "evaluations") {
loadRuns();
if (languages.length > 0 && languages[0]?.id) {
setDatasetLanguageId(languages[0].id);
}
}, [apiKeys, activeTab]);
}, [languages]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Potential race condition with language initialization.

The effect unconditionally sets datasetLanguageId whenever languages changes. If the user manually selects a language before languages finishes loading, their selection will be overridden. While unlikely in practice (languages loads quickly on mount), consider guarding the effect:

useEffect(() => {
  if (languages.length > 0 && languages[0]?.id && datasetLanguageId === 1) {
    setDatasetLanguageId(languages[0].id);
  }
}, [languages, datasetLanguageId]);

This preserves user selection unless still at the default fallback value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(main)/speech-to-text/page.tsx around lines 68 - 72, The effect that
updates datasetLanguageId (useEffect) will overwrite a user-selected language
whenever languages changes; modify the effect so it only sets
setDatasetLanguageId(languages[0].id) when the current datasetLanguageId is
still the default/uninitialized value (e.g., 1 or null) to avoid clobbering user
selection, and include datasetLanguageId in the dependency array so the guard
uses the latest value; keep references to useEffect, languages,
datasetLanguageId, and setDatasetLanguageId when making the change.

e.currentTarget.style.backgroundColor = colors.accent.primary;
}}
>
<Button variant="primary" size="md" onClick={() => router.back()}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a fallback when browser history is empty

router.back() may do nothing for direct-entry visits, which can trap users on this page. Add a fallback route (e.g., home or dashboard) when history is unavailable.

Suggested patch
-          <Button variant="primary" size="md" onClick={() => router.back()}>
+          <Button
+            variant="primary"
+            size="md"
+            onClick={() => {
+              if (window.history.length > 1) router.back();
+              else router.push("/");
+            }}
+          >
             ← Go Back
           </Button>
📝 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
<Button variant="primary" size="md" onClick={() => router.back()}>
<Button
variant="primary"
size="md"
onClick={() => {
if (window.history.length > 1) router.back();
else router.push("/");
}}
>
Go Back
</Button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/ComingSoon.tsx` at line 60, The Button in the ComingSoon
component currently calls router.back() which can do nothing for direct-entry
visits; update the onClick handler for the Button (in ComingSoon) to attempt
router.back() and fall back to a safe route (e.g., router.push('/') or
router.replace('/dashboard')) when history is absent—detect with a check like
window.history.length <= 1 or by awaiting router.back() and then calling the
fallback; ensure you reference the same Button onClick and the router instance
used in ComingSoon.

Comment on lines +35 to +42
<div className="flex items-center justify-between mb-4">
<h2 className="text-base font-semibold text-text-primary">
Your Datasets
</h2>
<Button variant="primary" size="sm" onClick={onUploadNew}>
<PlusIcon className="w-4 h-4" />
Upload New Dataset
</Button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Disable or hide the upload CTA when auth is missing.

The component renders a “Login required” state, but the header still lets unauthenticated users open the upload flow.

Proposed fix
-          <Button variant="primary" size="sm" onClick={onUploadNew}>
+          <Button
+            variant="primary"
+            size="sm"
+            onClick={onUploadNew}
+            disabled={!isAuthenticated}
+          >
             <PlusIcon className="w-4 h-4" />
             Upload New Dataset
           </Button>
📝 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
<div className="flex items-center justify-between mb-4">
<h2 className="text-base font-semibold text-text-primary">
Your Datasets
</h2>
<Button variant="primary" size="sm" onClick={onUploadNew}>
<PlusIcon className="w-4 h-4" />
Upload New Dataset
</Button>
<div className="flex items-center justify-between mb-4">
<h2 className="text-base font-semibold text-text-primary">
Your Datasets
</h2>
<Button
variant="primary"
size="sm"
onClick={onUploadNew}
disabled={!isAuthenticated}
>
<PlusIcon className="w-4 h-4" />
Upload New Dataset
</Button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/datasets/DatasetListing.tsx` around lines 35 - 42, The Upload
CTA in DatasetListing (the Button that calls onUploadNew with the PlusIcon) is
still active when the component renders the "Login required" state; update
DatasetListing to check the auth state (e.g., the user or isAuthenticated
prop/state used to render the login message) and either hide the Button entirely
or render it disabled and no-op when auth is missing. Ensure the conditional
references the same auth variable used elsewhere in DatasetListing so
unauthenticated users cannot trigger onUploadNew.

Comment on lines +32 to +33
const fileInputRef = useRef<HTMLInputElement>(null);
const isDisabled = !selectedFile || !datasetName.trim() || isUploading;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear the hidden file input when the form resets.

Resetting selectedFile alone doesn't reset the native file input. Re-selecting the same CSV after closing/success can stop firing onChange.

Proposed fix
-import { useRef, type ChangeEvent } from "react";
+import { useEffect, useRef, type ChangeEvent } from "react";
@@
   const fileInputRef = useRef<HTMLInputElement>(null);
   const isDisabled = !selectedFile || !datasetName.trim() || isUploading;
+
+  useEffect(() => {
+    if (!selectedFile && fileInputRef.current) {
+      fileInputRef.current.value = "";
+    }
+  }, [selectedFile]);

Also applies to: 53-65

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/datasets/UploadDatasetModal.tsx` around lines 32 - 33, The
hidden native file input isn't being cleared when resetting the form, so
re-selecting the same CSV may not trigger onChange; update the
reset/close/success paths that currently clear selectedFile (references:
fileInputRef, selectedFile, datasetName, isUploading) to also clear the native
input by setting fileInputRef.current.value to an empty string (and keep
fileInputRef.current = null if you already null it), and ensure this is done
wherever selectedFile is reset (e.g., after successful upload and on modal
close) so the browser will fire onChange for the same file next time.

Comment on lines +36 to +42
<Modal
open={open}
onClose={onClose}
title="Upload New Dataset"
maxWidth="max-w-2xl"
maxHeight="max-h-[90vh]"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Block backdrop/Escape dismissal while upload is in flight.

Right now the footer disables Cancel, but the modal itself can still close through onClose. That allows the form to reset while the request is still running.

Proposed fix
     <Modal
       open={open}
-      onClose={onClose}
+      onClose={isUploading ? () => {} : onClose}
       title="Upload New Dataset"
       maxWidth="max-w-2xl"
       maxHeight="max-h-[90vh]"
     >
📝 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
<Modal
open={open}
onClose={onClose}
title="Upload New Dataset"
maxWidth="max-w-2xl"
maxHeight="max-h-[90vh]"
>
<Modal
open={open}
onClose={isUploading ? () => {} : onClose}
title="Upload New Dataset"
maxWidth="max-w-2xl"
maxHeight="max-h-[90vh]"
>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/datasets/UploadDatasetModal.tsx` around lines 36 - 42, The
modal can still close via backdrop click or Escape even when an upload is in
flight; update UploadDatasetModal so Modal respects the upload state: either
pass Modal props to disable backdrop/Escape dismissal if supported (e.g.,
disableBackdropClick/disableEscapeKeyDown or equivalent) or guard the onClose
handler so it becomes a no-op while uploadInFlight/isUploading is true; ensure
you reference the Modal usage and the onClose prop in UploadDatasetModal and use
the existing upload state (uploadInFlight/isUploading) to prevent premature
closing.

Comment on lines +82 to +95
const handleSelectVersion = async (item: ConfigVersionItems) => {
const full = savedConfigs.find(
(c) => c.config_id === item.config_id && c.version === item.version,
);
const config =
full ??
(loadSingleVersion
? await loadSingleVersion(item.config_id, item.version)
: null);
if (config) {
onLoadConfig(config);
setIsOpen(false);
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Silent failure when loadSingleVersion returns null.

If savedConfigs does not contain the requested version and loadSingleVersion either isn't provided or resolves to null (e.g., network error, missing version), handleSelectVersion returns without invoking onLoadConfig and without closing the dropdown — the user gets no feedback and the click appears to do nothing. Consider surfacing an error toast and/or closing the dropdown in that path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/prompt-editor/LoadConfigDropdown.tsx` around lines 82 - 95,
handleSelectVersion can silently do nothing when a requested config isn't found
and loadSingleVersion is absent or returns null; update handleSelectVersion to
handle that error path by showing user feedback and closing the dropdown: if
full is falsy and either loadSingleVersion is undefined or returns null, call
setIsOpen(false) and surface an error (e.g., trigger the existing
toast/notification utility or call a provided onError handler) before returning,
otherwise proceed to call onLoadConfig(config) as before; reference
handleSelectVersion, savedConfigs, loadSingleVersion, onLoadConfig, and
setIsOpen when making the changes.

Comment on lines +40 to +44
{tools.map((tool, index) => (
<div
key={index}
className="p-3 rounded mb-2 border border-border bg-bg-secondary"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid array index as React key for a removable list.

Tools are removable via onRemoveTool(index), so using key={index} makes React reconcile by position. Combined with showTooltip (also keyed by index) and the controlled Field/<input> children, this causes tooltip / focus / IME composition state to attach to whichever tool now occupies that slot after a removal. Prefer a stable per-tool identifier — either an id on the Tool type or a synthetic stable key generated when the tool is added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/prompt-editor/ToolsSection.tsx` around lines 40 - 44, The list
rendering in tools.map uses key={index}, which breaks reconciliation when items
are removed (see tools.map, onRemoveTool(index), showTooltip and Field/input),
so replace the positional key with a stable per-tool identifier: add an id
property to the Tool objects when they are created (or generate a stable id in
the code path that adds tools) and use key={tool.id} in the map and for any
related keyed elements like showTooltip; ensure all usages that relied on index
for identity (tooltip keys, focused/controlled Field inputs) switch to tool.id
so focus/IME/tooltip state stays consistent after removals.

Comment on lines +87 to +98
<input
type="number"
value={tool.max_num_results}
onChange={(e) =>
onUpdateTool(
index,
"max_num_results",
parseInt(e.target.value) || 20,
)
}
className="w-full px-3 py-2 rounded-lg border border-border bg-bg-primary text-text-primary text-sm placeholder:text-neutral-400 focus:outline-none focus:ring-accent-primary/20 focus:border-accent-primary transition-colors"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Max Results input has fragile parsing and no bounds.

A few issues with the numeric input:

  • parseInt(e.target.value) || 20 snaps the value back to 20 whenever the user clears the field or types 0, which makes editing the value awkward (the field can't be transiently empty).
  • parseInt is missing an explicit radix.
  • No min/step attributes, so the spinner allows negatives and decimals that parseInt will silently truncate.
♻️ Suggested change
               <input
                 type="number"
+                min={1}
+                step={1}
                 value={tool.max_num_results}
                 onChange={(e) =>
-                  onUpdateTool(
-                    index,
-                    "max_num_results",
-                    parseInt(e.target.value) || 20,
-                  )
+                  onUpdateTool(
+                    index,
+                    "max_num_results",
+                    e.target.value === ""
+                      ? 20
+                      : Math.max(1, parseInt(e.target.value, 10) || 20),
+                  )
                 }
📝 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
<input
type="number"
value={tool.max_num_results}
onChange={(e) =>
onUpdateTool(
index,
"max_num_results",
parseInt(e.target.value) || 20,
)
}
className="w-full px-3 py-2 rounded-lg border border-border bg-bg-primary text-text-primary text-sm placeholder:text-neutral-400 focus:outline-none focus:ring-accent-primary/20 focus:border-accent-primary transition-colors"
/>
<input
type="number"
min={1}
step={1}
value={tool.max_num_results}
onChange={(e) =>
onUpdateTool(
index,
"max_num_results",
e.target.value === ""
? 20
: Math.max(1, parseInt(e.target.value, 10) || 20),
)
}
className="w-full px-3 py-2 rounded-lg border border-border bg-bg-primary text-text-primary text-sm placeholder:text-neutral-400 focus:outline-none focus:ring-accent-primary/20 focus:border-accent-primary transition-colors"
/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/prompt-editor/ToolsSection.tsx` around lines 87 - 98, The
numeric input handling for tool.max_num_results is fragile: stop using
parseInt(e.target.value) || 20 because it forces 20 for empty or "0" and lacks
radix/constraints; instead, keep the raw input when e.target.value === "" so the
field can be temporarily empty, parse with parseInt(value, 10) only when value
!== "" and Number.isInteger(parsed) to produce a sanitized integer, clamp with
Math.max(0, parsed) if you want non-negative results, and call
onUpdateTool(index, "max_num_results", sanitizedValue) (or null/undefined for
empty) accordingly; also add input attributes min={0} step={1} to the <input> to
prevent negatives/decimals and make typing predictable.

Comment on lines +74 to +83
const tools = currentConfigBlob.completion.params.tools || [];
const allKnowledgeBaseIds: string[] = [];
let maxNumResults = 20;

tools.forEach((tool) => {
allKnowledgeBaseIds.push(...tool.knowledge_base_ids);
if (allKnowledgeBaseIds.length === tool.knowledge_base_ids.length) {
maxNumResults = tool.max_num_results;
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Obscure max_num_results aggregation — looks like a latent bug.

The condition allKnowledgeBaseIds.length === tool.knowledge_base_ids.length is only true on the very first iteration that pushes anything (i.e., when allKnowledgeBaseIds was empty just before the push). For two or more tools it silently falls through, so maxNumResults ends up taking only the first tool's value while every tool's knowledge_base_ids are flattened. If the intent is "use the first tool's max", say so explicitly; if the intent is "use the last/aggregate", the code does the wrong thing.

♻️ Suggested rewrite (assumes "first tool wins")
-      const tools = currentConfigBlob.completion.params.tools || [];
-      const allKnowledgeBaseIds: string[] = [];
-      let maxNumResults = 20;
-
-      tools.forEach((tool) => {
-        allKnowledgeBaseIds.push(...tool.knowledge_base_ids);
-        if (allKnowledgeBaseIds.length === tool.knowledge_base_ids.length) {
-          maxNumResults = tool.max_num_results;
-        }
-      });
+      const tools = currentConfigBlob.completion.params.tools || [];
+      const allKnowledgeBaseIds: string[] = tools.flatMap(
+        (t) => t.knowledge_base_ids,
+      );
+      // Multiple file_search tools are flattened on the wire; preserve the
+      // first tool's max_num_results as the canonical value.
+      const maxNumResults = tools[0]?.max_num_results ?? 20;
📝 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 tools = currentConfigBlob.completion.params.tools || [];
const allKnowledgeBaseIds: string[] = [];
let maxNumResults = 20;
tools.forEach((tool) => {
allKnowledgeBaseIds.push(...tool.knowledge_base_ids);
if (allKnowledgeBaseIds.length === tool.knowledge_base_ids.length) {
maxNumResults = tool.max_num_results;
}
});
const tools = currentConfigBlob.completion.params.tools || [];
const allKnowledgeBaseIds: string[] = tools.flatMap(
(t) => t.knowledge_base_ids,
);
// Multiple file_search tools are flattened on the wire; preserve the
// first tool's max_num_results as the canonical value.
const maxNumResults = tools[0]?.max_num_results ?? 20;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/hooks/useConfigPersistence.ts` around lines 74 - 83, The aggregation of
max_num_results is incorrect: the forEach with the condition
allKnowledgeBaseIds.length === tool.knowledge_base_ids.length only matches on
the first pushed tool and is unclear; instead explicitly pick the first tool's
max_num_results (or clearly document a different policy). Replace the loop logic
around currentConfigBlob.completion.params.tools so that you compute
allKnowledgeBaseIds by flattening tool.knowledge_base_ids (e.g., using flatMap
or reduce) and set maxNumResults = tools[0].max_num_results when tools.length >
0 (or if you intended "last tool wins", assign maxNumResults =
tool.max_num_results on each iteration); adjust references to tools,
allKnowledgeBaseIds, and maxNumResults accordingly.

@Ayush8923 Ayush8923 merged commit cf89bae into feat/configuration-revamp May 11, 2026
2 checks passed
@Ayush8923 Ayush8923 deleted the feat/update-inline-styles branch May 11, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants