Skip to content

Comments

feat: initial setup wizard#1693

Merged
yottahmd merged 9 commits intomainfrom
setup-flow
Feb 21, 2026
Merged

feat: initial setup wizard#1693
yottahmd merged 9 commits intomainfrom
setup-flow

Conversation

@yottahmd
Copy link
Collaborator

@yottahmd yottahmd commented Feb 21, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Two-step setup wizard: admin account creation followed by AI agent configuration
    • Enable/disable AI agent and select preferred AI provider and model during initial setup
    • Chat input automatically pre-fills and opens when navigating from setup to the main application

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a two-step setup flow where users first create an admin account, then optionally configure an AI agent. It adds configuration update capabilities and integrates agent chat auto-opening via state-based navigation when redirected from setup. The setup process validates configuration, calls remote APIs to configure the agent, updates global config state, and navigates to the main page with prefilled agent chat.

Changes

Cohort / File(s) Summary
Configuration Management
ui/src/App.tsx, ui/src/contexts/ConfigContext.tsx
Introduces ConfigUpdateContext and useUpdateConfig hook to enable partial config updates via context. App.tsx wires the updateConfig callback into the provider tree.
Agent Chat Input Management
ui/src/features/agent/components/AgentChatModal.tsx, ui/src/features/agent/components/ChatInput.tsx, ui/src/features/agent/context/AgentChatContext.tsx
Adds initialInputValue state to AgentChatContext; ChatInput accepts optional initialValue prop for pre-population; AgentChatModal clears initial value after sending messages.
Layout and Navigation
ui/src/layouts/Layout.tsx
Adds auto-open logic that detects openAgent state from location, pre-fills agent chat input, opens modal, and cleans up navigation state.
Setup Page Redesign
ui/src/pages/setup.tsx
Restructures to two-step flow: Step 1 creates admin account; Step 2 configures AI agent with provider/model selection, API key handling, and preset loading. Updates global config and navigates with state to trigger agent chat auto-open.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SetupPage as Setup Page
    participant RemoteAPI as Remote API
    participant ConfigCtx as Config Context
    participant Layout
    participant AgentChat as Agent Chat

    User->>SetupPage: Complete admin setup (Step 1)
    SetupPage->>SetupPage: Navigate to Step 2
    User->>SetupPage: Enable agent & configure (Step 2)
    SetupPage->>RemoteAPI: Fetch model presets
    RemoteAPI-->>SetupPage: Presets loaded
    User->>SetupPage: Submit agent config
    SetupPage->>RemoteAPI: POST create model config
    RemoteAPI-->>SetupPage: Config created
    SetupPage->>RemoteAPI: PUT set as default model
    RemoteAPI-->>SetupPage: Updated
    SetupPage->>ConfigCtx: updateConfig(agentEnabled)
    ConfigCtx-->>SetupPage: Config updated
    SetupPage->>Layout: Navigate with state{openAgent, initialMessage}
    
    Layout->>AgentChat: Detect openAgent in location state
    Layout->>AgentChat: setInitialInputValue(message)
    Layout->>AgentChat: openChat()
    AgentChat->>AgentChat: Pre-fill input & open modal
    Layout->>Layout: Clear navigation state
Loading
sequenceDiagram
    participant User
    participant AgentChatModal as AgentChatModal
    participant ChatInput
    participant AgentChatContext as Context

    AgentChatModal->>AgentChatContext: Read initialInputValue
    AgentChatContext-->>ChatInput: Provide initialInputValue
    ChatInput->>ChatInput: useEffect: Pre-fill textarea
    ChatInput-->>User: Display pre-filled input
    
    User->>ChatInput: Type/edit message
    User->>AgentChatModal: Click send
    AgentChatModal->>AgentChatModal: Send message
    AgentChatModal->>AgentChatContext: setInitialInputValue(null)
    AgentChatContext-->>ChatInput: Clear initial value
    ChatInput->>ChatInput: useEffect: Reset textarea
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: initial setup wizard' accurately summarizes the main change—introducing a multi-step setup wizard for initial configuration, as evidenced by the substantial setup.tsx changes and supporting infrastructure additions.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch setup-flow

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

🧹 Nitpick comments (4)
ui/src/layouts/Layout.tsx (1)

86-92: Hardcoded prompt string should be a constant or passed via navigation state.

The string 'Create a simple DAG that prints Hello World every minute' on Line 88 is a magic string embedded in layout logic. If the setup page wants to customize this message in the future, it would need to change Layout.tsx. Consider passing it through location.state from setup.tsx:

♻️ Suggested approach — use location state for the message

In setup.tsx Line 230:

-      navigate('/', { replace: true, state: { openAgent: true } });
+      navigate('/', { replace: true, state: { openAgent: true, agentPrompt: 'Create a simple DAG that prints Hello World every minute' } });

In Layout.tsx:

   React.useEffect(() => {
-    if ((location.state as { openAgent?: boolean })?.openAgent) {
-      setInitialInputValue('Create a simple DAG that prints Hello World every minute');
+    const state = location.state as { openAgent?: boolean; agentPrompt?: string } | null;
+    if (state?.openAgent) {
+      if (state.agentPrompt) {
+        setInitialInputValue(state.agentPrompt);
+      }
       openChat();
       navigate(location.pathname, { replace: true, state: {} });
     }
   }, [location.state, location.pathname, openChat, setInitialInputValue, navigate]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/src/layouts/Layout.tsx` around lines 86 - 92, Replace the hardcoded prompt
string in the Layout useEffect with a configurable value: read a prompt from
location.state (e.g. (location.state as { openAgent?: boolean, initialPrompt?:
string })?.initialPrompt) and fall back to a shared constant exported from the
file (or a constants module) if not provided; update the effect to call
setInitialInputValue with that variable before openChat and leave references to
setInitialInputValue, openChat and navigate unchanged so behavior is preserved.
ui/src/pages/setup.tsx (2)

84-98: fetchPresets swallows all errors silently.

The catch {} block (Line 93) means a 401/403 error (expired auth) or a network failure would be invisible to the user. Consider at minimum logging to console, or showing a non-blocking warning so the user knows presets couldn't be loaded.

♻️ Suggested improvement
     } catch {
-      // Presets are optional, don't block the user
+      // Presets are optional — log but don't block the user
+      console.warn('Failed to fetch model presets');
     } finally {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/src/pages/setup.tsx` around lines 84 - 98, The fetchPresets function
currently swallows all errors in its catch block; update the catch to capture
the error (e.g., catch (err)) and at minimum log it (console.warn or process
logger) and/or set a non-blocking UI flag/toast so the user knows presets
couldn't be loaded without blocking them; make changes around the fetchPresets
function, the catch block, and any related state (setPresetsLoading remains in
finally) and use client.GET error information when logging or populating the
warning.

183-195: Replace Record<string, unknown> with the CreateModelConfigRequest schema type.

The code constructs an object matching the API request schema but uses an untyped Record<string, unknown>. Since the POST to /settings/agent/models expects CreateModelConfigRequest, use const modelBody: components['schemas']['CreateModelConfigRequest'] = { ... } to catch shape mismatches at compile time. The necessary components import is already present.

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

In `@ui/src/pages/setup.tsx` around lines 183 - 195, Change the type of modelBody
from the loose Record<string, unknown> to the generated API schema type so
TypeScript verifies the shape: replace the declaration of modelBody with const
modelBody: components['schemas']['CreateModelConfigRequest'] = { ... } (using
the existing components import), keeping the same properties (id from
generateSlugId(preset.name), name, provider, model, apiKey.trim(),
description/contextWindow/maxOutputTokens/inputCostPer1M/outputCostPer1M as
optional/undefined, supportsThinking defaulting to false) and adjust any
property names or optionality to match CreateModelConfigRequest if the compiler
flags mismatches.
ui/src/App.tsx (1)

95-99: Shallow merge won't handle nested config fields correctly.

updateConfig uses a shallow spread ({ ...prev, ...patch }). If a future caller passes a partial permissions or paths object, it will replace the entire nested object rather than merging into it. Currently only agentEnabled (a flat boolean) is patched, so this is safe today, but consider documenting or guarding against nested partial patches.

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

In `@ui/src/App.tsx` around lines 95 - 99, The updateConfig function in AppInner
currently does a shallow merge via setConfig((prev) => ({ ...prev, ...patch })),
which will replace nested objects like permissions or paths instead of merging
them; change updateConfig to perform a deep/recursive merge (or merge specific
nested keys) so partial updates to nested fields are merged into prev rather
than overwritten — e.g., use a utility such as lodash.merge or a simple
recursiveMerge helper and call setConfig(prev => merge({}, prev, patch)); keep
references to AppInner, updateConfig, config, setConfig, and nested keys like
permissions, paths (agentEnabled is currently flat) and add a comment
documenting that updateConfig performs deep merges for nested config fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ui/src/features/agent/components/ChatInput.tsx`:
- Around line 93-98: The useEffect in ChatInput sets state with
setMessage(initialValue) but doesn't update the textarea's height; after
pre-filling call the same resize logic used by the textarea onInput so the
height recalculates. Locate the textarea ref and the resize function (e.g.,
resizeTextarea or the onInput handler) in the ChatInput component and invoke it
after setMessage(initialValue) — either directly using the textareaRef (if
available) or by scheduling the onInput/resize call (e.g., with
requestAnimationFrame or setTimeout 0) so the DOM reflects the new value and the
textarea auto-resizes to fit the pre-filled text.

In `@ui/src/pages/setup.tsx`:
- Around line 37-43: generateSlugId can return an empty string for inputs like
"!!!" or whitespace, causing invalid model ids; update the function so after
sanitization it guarantees a non-empty slug (for example fallback to a
deterministic placeholder/fallback such as "model-<timestamp>" or
"model-<randomSuffix>" or throw a validation error) and return that instead of
an empty string so calls that use generateSlugId for model id creation always
receive a valid id; change the logic inside generateSlugId to check the
sanitized result and substitute the chosen fallback before returning.
- Around line 146-153: handleStep2Submit currently short-circuits when
agentEnabled is false and skips updating the server, leaving a previously
enabled agent still enabled; modify handleStep2Submit so that when agentEnabled
is false it issues the same PATCH/update call used when enabling (or a dedicated
PATCH to set enabled: false) before returning/navigating, ensuring the
server-side agent state is set to disabled (refer to the existing PATCH/update
logic used on enable within handleStep2Submit or its helper function and reuse
it to send enabled: false).
- Around line 198-212: Replace the raw fetch call that assigns createResp and
subsequent createResp.json() with the typed client.POST call so the request uses
the codebase auth/error middleware and includes remoteNode as a query param;
call client.POST('/settings/agent/models', { remoteNode }, modelBody) (or the
established client.POST signature used elsewhere in this file), remove
getAuthHeaders/config.apiURL usage, and assign the parsed response to
createdModel; ensure you handle parse errors by catching the client response
parsing or wrap awaiting the POST in try/catch and throw a new
Error(response.message || 'Failed to create model') so malformed JSON is handled
consistently with other client calls.

---

Nitpick comments:
In `@ui/src/App.tsx`:
- Around line 95-99: The updateConfig function in AppInner currently does a
shallow merge via setConfig((prev) => ({ ...prev, ...patch })), which will
replace nested objects like permissions or paths instead of merging them; change
updateConfig to perform a deep/recursive merge (or merge specific nested keys)
so partial updates to nested fields are merged into prev rather than overwritten
— e.g., use a utility such as lodash.merge or a simple recursiveMerge helper and
call setConfig(prev => merge({}, prev, patch)); keep references to AppInner,
updateConfig, config, setConfig, and nested keys like permissions, paths
(agentEnabled is currently flat) and add a comment documenting that updateConfig
performs deep merges for nested config fields.

In `@ui/src/layouts/Layout.tsx`:
- Around line 86-92: Replace the hardcoded prompt string in the Layout useEffect
with a configurable value: read a prompt from location.state (e.g.
(location.state as { openAgent?: boolean, initialPrompt?: string
})?.initialPrompt) and fall back to a shared constant exported from the file (or
a constants module) if not provided; update the effect to call
setInitialInputValue with that variable before openChat and leave references to
setInitialInputValue, openChat and navigate unchanged so behavior is preserved.

In `@ui/src/pages/setup.tsx`:
- Around line 84-98: The fetchPresets function currently swallows all errors in
its catch block; update the catch to capture the error (e.g., catch (err)) and
at minimum log it (console.warn or process logger) and/or set a non-blocking UI
flag/toast so the user knows presets couldn't be loaded without blocking them;
make changes around the fetchPresets function, the catch block, and any related
state (setPresetsLoading remains in finally) and use client.GET error
information when logging or populating the warning.
- Around line 183-195: Change the type of modelBody from the loose
Record<string, unknown> to the generated API schema type so TypeScript verifies
the shape: replace the declaration of modelBody with const modelBody:
components['schemas']['CreateModelConfigRequest'] = { ... } (using the existing
components import), keeping the same properties (id from
generateSlugId(preset.name), name, provider, model, apiKey.trim(),
description/contextWindow/maxOutputTokens/inputCostPer1M/outputCostPer1M as
optional/undefined, supportsThinking defaulting to false) and adjust any
property names or optionality to match CreateModelConfigRequest if the compiler
flags mismatches.

@yottahmd yottahmd merged commit 25850df into main Feb 21, 2026
2 checks passed
@yottahmd yottahmd deleted the setup-flow branch February 21, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant