Skip to content

Simplify databunny api.#92

Merged
izadoesdev merged 3 commits into
databuddy-analytics:stagingfrom
sbansal1999:ai-flow-simplifcation
Aug 17, 2025
Merged

Simplify databunny api.#92
izadoesdev merged 3 commits into
databuddy-analytics:stagingfrom
sbansal1999:ai-flow-simplifcation

Conversation

@sbansal1999

@sbansal1999 sbansal1999 commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Had to add default chart type as bar, because for some reason the LLM response was not giving one.

Might be solved by making use of discriminated unions in the schema.

Summary by CodeRabbit

  • Bug Fixes

    • Chart responses now default to a bar chart when chart type is missing, improving reliability.
  • Documentation

    • Assistant streaming API: send a messages array (role: 'user' | 'assistant', content) and websiteId (camelCase) instead of message/context and website_id.
  • Refactor

    • Assistant backend request and prompt flow streamlined (no user-visible behavior changes).
  • Style

    • Dashboard assistant icons standardized to Icon variants; minor layout tweak in Websites Overview (removed top padding).

@vercel

vercel Bot commented Aug 16, 2025

Copy link
Copy Markdown

@sbansal1999 is attempting to deploy a commit to the Databuddy Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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.

Walkthrough

Refactors assistant request shape to use a messages array and camelCased websiteId/websiteHostname, replaces unified prompt with a system prompt, updates AI client/processor flow to use getAICompletion, renames prompt export, defaults chart handler chartType to 'bar', and updates dashboard streaming payload and icon names; minor layout spacing change.

Changes

Cohort / File(s) Summary of changes
Assistant request schema & typing
apps/api/src/schemas/assistant-schemas.ts
Replace top-level message/website_id/context with messages[] and websiteId; tighten message role to `'user'
Route: assistant input mapping
apps/api/src/routes/assistant.ts
Read messages, websiteId, model; validate site with websiteId; construct AssistantRequest with messages, websiteId, websiteHostname, model (no context).
Processor: request/response flow
apps/api/src/agent/processor.ts
AssistantRequest now extends AssistantRequestType and adds websiteHostname; removed manual prompt assembly; call getAICompletion(request); parse AI content and branch on response_type (text/metric/chart); updated logging to use new fields.
AI client: system prompt & messages
apps/api/src/agent/utils/ai-client.ts
getAICompletion now accepts AssistantRequest; build systemPrompt via comprehensiveSystemPrompt(request.websiteId, request.websiteHostname, 'execute_chat', request.model); chat messages = [system, ...request.messages]; temperature fixed to 0.1.
Prompts: rename and signature change
apps/api/src/agent/prompts/agent.ts, apps/api/src/agent/index.ts
Rename comprehensiveUnifiedPromptcomprehensiveSystemPrompt; shrink signature to (websiteId, websiteHostname, mode, _model) and remove userQuery/previousMessages/agent_tool_result; update re-export in index.
Chart handler tweak
apps/api/src/agent/handlers/chart-handler.ts
data.chartType now defaults to 'bar' when parsedAiJson.chart_type is falsy; completion message display type logic unchanged.
Dashboard: streaming payload
apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
Send consolidated messages[] with roles mapped from history plus current { role: 'user', content }; use websiteId; remove context.
Dashboard: icon component renames
apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
Replace icon imports/usages with *Icon variants (e.g., BrainBrainIcon, ChartBarChartBarIcon); no logic changes.
Dashboard: layout spacing
apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
Remove pt-6 from container class (drop top padding).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Dashboard as Dashboard (use-chat)
  participant API as API /v1/assistant/stream
  participant Processor
  participant AIClient as AI Client
  participant LLM as AI Model
  participant Handlers as Metric/Chart Handlers

  User->>Dashboard: Type message
  Dashboard->>API: POST { messages[], websiteId, model }
  API->>Processor: AssistantRequest { messages, websiteId, websiteHostname, model }
  Processor->>AIClient: getAICompletion(request)
  AIClient->>AIClient: Build system prompt (comprehensiveSystemPrompt)
  AIClient->>LLM: messages = [system, ...request.messages]
  LLM-->>AIClient: aiResponse
  AIClient-->>Processor: content + meta
  Processor->>Processor: Parse response_type
  alt text
    Processor-->>API: stream text response
  else metric
    Processor->>Handlers: handleMetricResponse
    Handlers-->>Processor: metric result
    Processor-->>API: stream metric response
  else chart
    Processor->>Handlers: handleChartResponse(sql, timing)
    Handlers-->>Processor: chart result
    Processor-->>API: stream chart response
  else invalid
    Processor-->>API: error response
  end
  API-->>Dashboard: SSE stream
  Dashboard-->>User: Render assistant reply
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🔭 Outside diff range comments (1)
apps/api/src/agent/handlers/chart-handler.ts (1)

67-76: Align completion message with the defaulted chartType to avoid user-facing mismatch

When chart_type is absent, data.chartType defaults to 'bar', but the completion message falls back to 'chart'. This can confuse users. Compute a single resolved chart type and use it consistently in both places.

Apply this diff:

   yield {
     type: 'complete',
-    content:
-      queryResult.data.length > 0
-        ? `Found ${queryResult.data.length} data points. Displaying as a ${parsedAiJson.chart_type ? parsedAiJson.chart_type.replace(/_/g, ' ') : 'chart'}.`
-        : getRandomMessage(noDataMessages),
+    // Use a single resolved chart type for message and payload
+    content: (() => {
+      const resolvedChartType =
+        (parsedAiJson.chart_type?.trim() as string | undefined) || 'bar';
+      return queryResult.data.length > 0
+        ? `Found ${queryResult.data.length} data points. Displaying as a ${resolvedChartType.replace(/_/g, ' ')}.`
+        : getRandomMessage(noDataMessages);
+    })(),
     data: {
       hasVisualization: queryResult.data.length > 0,
-      chartType: parsedAiJson.chart_type || 'bar',
+      chartType:
+        (parsedAiJson.chart_type?.trim() as string | undefined) || 'bar',
       data: queryResult.data,
       responseType: 'chart',
     },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5923342 and 7e92487.

📒 Files selected for processing (10)
  • apps/api/src/agent/handlers/chart-handler.ts (1 hunks)
  • apps/api/src/agent/index.ts (1 hunks)
  • apps/api/src/agent/processor.ts (3 hunks)
  • apps/api/src/agent/prompts/agent.ts (2 hunks)
  • apps/api/src/agent/utils/ai-client.ts (2 hunks)
  • apps/api/src/routes/assistant.ts (2 hunks)
  • apps/api/src/schemas/assistant-schemas.ts (1 hunks)
  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx (1 hunks)
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx (8 hunks)
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't pass children as props.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't forget key props in iterators and collection literals.
Don't destructure props inside JSX components in Solid projects.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign to React component props.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use dangerous JSX props.
Don't use Array index in keys.
Don't insert comments as text nodes.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Watch out for possible "wrong" semicolons inside JSX elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Don't use distracting elements like or .
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible ...

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,jsx,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't use global eval().
Don't use var.
Don't use console.
Don't use debugger.
Don't assign directly to document.cookie.
Don't use duplicate case labels.
Don't use duplicate class members.
Don't use duplicate function parameter names.
Don't use empty block statements and static blocks.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Use for...of statements instead of Array.forEach.
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables.
Don't use constant expressions in conditions.
Don't use Math.min and Math.max to clamp values when the result is constant.
Don't return a value from a constructor.
Don't use empty character classes in regular expression literals.
Don't use empty destructuring patterns.
Don't call global object properties as functions.
Don't declare functions and vars that are accessible outside their block.
Don't use variables and function parameters before they're declared.
Don't use 8 and 9 escape sequences in string literals.
Don't use literal numbers that lose precision.
Don't use duplicate conditions in if-else-if chains.
Don't use two keys with the same name inside objects...

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use TypeScript namespaces.
Don't use the TypeScript directive @ts-ignore.
Don't use any type.
Don't use implicit any type on variable declarations.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't declare empty interfaces.
Use export type for types.
Use import type for types.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.

**/*.{ts,tsx}: Ensure TypeScript type-safety; avoid any unless absolutely necessary
Create proper interfaces/types for APIs, responses, and components; place them in shared types folders rather than in the same file

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use bun <file> instead of node <file> or ts-node <file>
Do not use dotenv; Bun automatically loads .env files
Do not use express; use Bun.serve() for HTTP servers
Do not use better-sqlite3; use bun:sqlite for SQLite
Do not use ioredis; use Bun.redis for Redis
Do not use pg or postgres.js; use Bun.sql for Postgres
Do not use ws; use built-in WebSocket
Prefer Bun.file over node:fs's readFile/writeFile
Use Bun.$ instead of execa for running shell commands

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/01-MUST-DO.mdc)

**/*.{tsx,jsx}: In Tailwind CSS usage, use rounded only; do not use rounded-md or rounded-xl
Use Phosphor icons, not Lucide; default to weight="duotone", use fill for arrows, and for plus icons do not set weight
In view components, decouple state management, data transformations, and API interactions from the React lifecycle
Simplify data flow in components to avoid prop drilling and callback chains
Prioritize modularity and testability in all React components
Use React Error Boundaries appropriately
Avoid useEffect in React unless it is truly critical

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/01-MUST-DO.mdc)

**/*.{ts,tsx,js,jsx}: Use console methods appropriately (e.g., console.error, console.time, console.table, console.json)
Use Dayjs instead of date-fns
Use TanStack Query for hooks; do not use SWR
When adding debugging, use JSON.stringify() for structured output

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{tsx,jsx,ts,js}

📄 CodeRabbit Inference Engine (.cursor/rules/01-MUST-DO.mdc)

When using Phosphor React icons, append 'Icon' to component names (e.g., CaretIcon, not Caret)

Files:

  • apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx
  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{js,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts}: Make sure to use the "use strict" directive in script files.
Don't have redundant "use strict".
Make sure to use the "use strict" directive in script files.

Files:

  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
**/*.{html,ts,css}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • apps/api/src/agent/index.ts
  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/processor.ts
  • apps/api/src/agent/prompts/agent.ts
🧠 Learnings (2)
📚 Learning: 2025-08-11T10:37:28.419Z
Learnt from: CR
PR: databuddy-analytics/Databuddy#0
File: .cursor/rules/01-MUST-DO.mdc:0-0
Timestamp: 2025-08-11T10:37:28.419Z
Learning: Applies to **/*.{tsx,jsx,ts,js} : When using Phosphor React icons, append 'Icon' to component names (e.g., CaretIcon, not Caret)

Applied to files:

  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
📚 Learning: 2025-08-11T10:37:28.419Z
Learnt from: CR
PR: databuddy-analytics/Databuddy#0
File: .cursor/rules/01-MUST-DO.mdc:0-0
Timestamp: 2025-08-11T10:37:28.419Z
Learning: Applies to **/*.{tsx,jsx} : Use Phosphor icons, not Lucide; default to weight="duotone", use fill for arrows, and for plus icons do not set weight

Applied to files:

  • apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx
🧬 Code Graph Analysis (5)
apps/api/src/routes/assistant.ts (1)
apps/api/src/lib/website-utils.ts (1)
  • validateWebsite (196-206)
apps/api/src/schemas/assistant-schemas.ts (1)
packages/rpc/src/trpc.ts (1)
  • t (27-32)
apps/api/src/agent/utils/ai-client.ts (2)
apps/api/src/agent/processor.ts (1)
  • AssistantRequest (26-28)
apps/api/src/agent/prompts/agent.ts (1)
  • comprehensiveSystemPrompt (45-636)
apps/api/src/agent/processor.ts (3)
apps/api/src/agent/index.ts (2)
  • AssistantRequest (5-5)
  • getAICompletion (12-12)
apps/api/src/schemas/assistant-schemas.ts (1)
  • AssistantRequestType (16-16)
apps/api/src/agent/utils/ai-client.ts (1)
  • getAICompletion (26-74)
apps/api/src/agent/prompts/agent.ts (2)
apps/api/src/agent/index.ts (1)
  • comprehensiveSystemPrompt (10-10)
apps/api/src/schemas/assistant-schemas.ts (1)
  • AssistantRequestType (16-16)
🔇 Additional comments (9)
apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx (1)

917-917: Confirm layout spacing remains consistent
Removing the top padding (pt-6) can cause the overview tab’s content to shift upward. Please verify:

  • There’s no overlap with any sticky headers or navigation bars
  • Vertical spacing still matches sibling tabs across all breakpoints
apps/api/src/routes/assistant.ts (1)

48-51: No fallback needed: website.domain is non-nullable
The shared Website type defines domain: string (non-optional), and our database model enforces a non-null value. Therefore website.domain can’t be undefined, so the existing code is safe as-is.

apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts (1)

185-197: Messages-based payload construction is correct; aligns with backend schema

Good switch to a unified messages array and camel-cased websiteId. Appending the current user message explicitly avoids the stale-state duplication pitfall since messages is the pre-send snapshot.

  • Please confirm the server route expects camelCase websiteId in the JSON body (and not website_id) and does not require websiteHostname from the client. If it does, we should include it here as well.
  • Also consider guarding against empty websiteId before sending to avoid avoidable 4xx responses.

If you want, I can add a small guard that early-returns with a user-friendly message when websiteId is missing.

apps/api/src/agent/utils/ai-client.ts (1)

31-36: System prompt integration LGTM

Injecting a single system message in front of the chat history matches the new flow and keeps the request lean. Keeping temperature fixed at 0.1 is fine for determinism.

apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx (2)

4-13: Icon import renames comply with project convention

Switching to Icon-suffixed Phosphor components matches the repository’s guideline and prior learnings.


101-111: Quick questions UI update LGTM

The quick actions reference the updated Icon variants and remain accessible, keyed, and guarded by rate-limit/loading states.

apps/api/src/agent/prompts/agent.ts (1)

2-2: Type import is appropriate

Tying the prompt generator’s optional _model param to AssistantRequestType['model'] keeps it aligned with schema changes.

apps/api/src/agent/processor.ts (2)

26-28: AssistantRequest interface extension looks good

Extending the schema type and adding websiteHostname here keeps the contract centralized and consistent for downstream consumers.


59-60: Verified AssistantRequest construction & chart defaulting

  • In apps/api/src/routes/assistant.ts, the POST /stream handler builds the AssistantRequest with
    • messages
    • websiteId
    • websiteHostname: website.domain
    • model (defaulting to 'chat')
  • In apps/api/src/agent/handlers/chart-handler.ts, chartType falls back to 'bar' when parsedAiJson.chart_type is falsy.

No changes required—approving as-is.

Comment thread apps/api/src/agent/index.ts
Comment thread apps/api/src/agent/processor.ts
Comment on lines +45 to 50
export const comprehensiveSystemPrompt = (
websiteId: string,
websiteHostname: string,
mode: 'analysis_only' | 'execute_chat' | 'execute_agent_step',
previousMessages?: Array<{ role: string; content: string }>,
agentToolResult?: Record<string, unknown>,
_model?: 'chat' | 'agent' | 'agent-max'
_model?: AssistantRequestType['model']
) => `

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prompt still references <user_query> and includes invalid ClickHouse INTERVAL examples—please fix

  • The new flow no longer supplies <user_query>; the prompt should instruct the model to use “the latest user message in the provided messages” instead of <user_query>.
  • Several SQL examples use Postgres-like INTERVAL literals (e.g., INTERVAL '7' DAY). ClickHouse syntax is INTERVAL 7 DAY (numeric, no quotes). This will lead to invalid queries if the model copies these patterns. There’s also a QUALIFY usage in an example that ClickHouse doesn’t support—prefer subqueries or window + WHERE/HAVING alternatives.

Suggested changes:

  • Replace occurrences of “process the <user_query>” with “process the latest user message in the conversation”.
  • Fix INTERVAL literals: '7' DAY7 DAY, '30' DAY30 DAY, etc.
  • Replace QUALIFY examples with ClickHouse-compatible patterns (CTEs or nested subqueries with ROW_NUMBER() in a subquery and filter in outer SELECT).

Additionally, to enforce correctness at the schema level (and reduce reliance on defaults), consider switching to Zod discriminated unions so chart responses must include a chart type:

// Outside this hunk: refactor AIResponseJsonSchema to a discriminated union
const Base = z.object({
  sql: z.string().nullable().optional(),
  thinking_steps: z.array(z.string()).optional(),
});

const TextResp = Base.extend({
  response_type: z.literal('text'),
  text_response: z.string().nullable().optional(),
  chart_type: z.null().optional(),
});

const MetricResp = Base.extend({
  response_type: z.literal('metric'),
  metric_value: z.string(),
  metric_label: z.string().nullable().optional(),
  text_response: z.string().nullable().optional(),
  chart_type: z.null().optional(),
});

const ChartResp = Base.extend({
  response_type: z.literal('chart'),
  sql: z.string(), // require SQL for charts
  chart_type: z.enum([/* …all chart types… */]).default('bar'), // enforce presence with default
  text_response: z.string().nullable().optional(),
});

export const AIResponseJsonSchema = z.discriminatedUnion('response_type', [
  TextResp, MetricResp, ChartResp,
]);

This aligns with the PR note about discriminated unions and ensures the LLM output is structurally valid for each response type.

Comment thread apps/api/src/agent/utils/ai-client.ts
Comment thread apps/api/src/routes/assistant.ts
Comment thread apps/api/src/schemas/assistant-schemas.ts Outdated
Comment thread apps/dashboard/app/(main)/websites/[id]/assistant/components/chat-section.tsx Outdated
@sbansal1999
sbansal1999 force-pushed the ai-flow-simplifcation branch from 7e92487 to 77bd3ab Compare August 16, 2025 22:03
@sbansal1999

Copy link
Copy Markdown
Contributor Author

will take a look at these tomorrow, do not merge till then

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🔭 Outside diff range comments (5)
apps/api/src/agent/handlers/chart-handler.ts (1)

71-76: Unify chartType fallback between display text and data payload

Currently, the user-facing message falls back to "chart" while data.chartType falls back to "bar". This creates inconsistent UX and can confuse the frontend. Use the same fallback ("bar") for both.

Apply this diff:

-					? `Found ${queryResult.data.length} data points. Displaying as a ${parsedAiJson.chart_type ? parsedAiJson.chart_type.replace(/_/g, ' ') : 'chart'}.`
+					? `Found ${queryResult.data.length} data points. Displaying as a ${(parsedAiJson.chart_type || 'bar').replace(/_/g, ' ')}.`

Follow-up: Consider validating chart_type against a known allowlist and/or switching to a discriminated union in the schema so invalid types are rejected early.

apps/api/src/agent/utils/ai-client.ts (1)

52-57: Avoid logging full AI content (PII and log volume risk).

Logging content can leak user data and balloon logs. Keep only metrics unless an admin/debug flag is set.

-    console.info('🤖 [AI Client] Completion completed', {
-      timeTaken: `${aiTime}ms`,
-      contentLength: JSON.stringify(content).length,
-      usage: chat.usage,
-      content,
-    });
+    console.info('🤖 [AI Client] Completion completed', {
+      timeTaken: `${aiTime}ms`,
+      contentLength: JSON.stringify(content).length,
+      usage: chat.usage,
+    });
apps/api/src/agent/prompts/agent.ts (3)

4-34: Enforce structural validity with a discriminated union; default chart_type to 'bar' only for charts.

Current schema allows incomplete/invalid combos (e.g., chart without SQL, metric without value). Move to a discriminated union and set chart_type default only on chart responses. This aligns with the PR goal and reduces downstream guard code.

-export const AIResponseJsonSchema = z.object({
-	sql: z.string().nullable().optional(),
-	chart_type: z
-		.enum([
-			'bar',
-			'horizontal_bar',
-			'line',
-			'sparkline',
-			'pie',
-			'donut',
-			'area',
-			'unstacked_area',
-			'stacked_bar',
-			'multi_line',
-			'scatter',
-			'bubble',
-			'radar',
-			'funnel',
-			'grouped_bar',
-			'histogram',
-			'treemap',
-			'gauge',
-		])
-		.nullable()
-		.optional(),
-	response_type: z.enum(['chart', 'text', 'metric']),
-	text_response: z.string().nullable().optional(),
-	metric_value: z.string().nullable().optional(),
-	metric_label: z.string().nullable().optional(),
-	thinking_steps: z.array(z.string()).optional(),
-});
+const Base = z.object({
+  thinking_steps: z.array(z.string()).optional(),
+});
+
+const TextResp = Base.extend({
+  response_type: z.literal('text'),
+  sql: z.null().optional(),
+  chart_type: z.null().optional(),
+  text_response: z.string().nullable().optional(),
+  metric_value: z.null().optional(),
+  metric_label: z.string().nullable().optional(),
+});
+
+const MetricResp = Base.extend({
+  response_type: z.literal('metric'),
+  sql: z.null().optional(),
+  chart_type: z.null().optional(),
+  metric_value: z.string(),
+  metric_label: z.string().nullable().optional(),
+  text_response: z.string().nullable().optional(),
+});
+
+const ChartResp = Base.extend({
+  response_type: z.literal('chart'),
+  sql: z.string(),
+  chart_type: z
+    .enum([
+      'bar','horizontal_bar','line','sparkline','pie','donut','area',
+      'unstacked_area','stacked_bar','multi_line','scatter','bubble',
+      'radar','funnel','grouped_bar','histogram','treemap','gauge',
+    ])
+    .default('bar'),
+  text_response: z.string().nullable().optional(),
+  metric_value: z.null().optional(),
+  metric_label: z.null().optional(),
+});
+
+export const AIResponseJsonSchema = z.discriminatedUnion('response_type', [
+  TextResp,
+  MetricResp,
+  ChartResp,
+]);

297-298: Use a valid enum value for chart_type; avoid “multi_line or stacked_bar”.

The schema enumerates discrete values. Provide one (e.g., multi_line) and let the assistant choose contextually.

-      <chart_type>multi_line or stacked_bar</chart_type>
+      <chart_type>multi_line</chart_type>

453-458: Fix reference to non-existent errors.id; use COUNT(*) instead.

The declared analytics.errors schema has no id column. Counting e.id will fail.

-            SELECT es.session_id, COUNT(e.id) as error_count
+            SELECT es.session_id, COUNT(*) as error_count
♻️ Duplicate comments (12)
apps/api/src/schemas/assistant-schemas.ts (1)

4-14: Tighten request validation: require non-empty messages, non-empty content, and disallow extra fields

Reiterating prior feedback: harden the schema to reject malformed inputs and simplify downstream assumptions.

Apply this diff:

-export const AssistantRequestSchema = t.Object({
-	messages: t.Array(
-		t.Object({
-			role: t.Union([t.Literal('user'), t.Literal('assistant')]),
-			content: t.String(),
-		})
-	),
-	websiteId: t.String(),
-	model: t.Optional(
-		t.Union([t.Literal('chat'), t.Literal('agent'), t.Literal('agent-max')])
-	)
-});
+export const AssistantRequestSchema = t.Object(
+	{
+		messages: t.Array(
+			t.Object(
+				{
+					role: t.Union([t.Literal('user'), t.Literal('assistant')]),
+					content: t.String({ minLength: 1 }),
+				},
+				{ additionalProperties: false }
+			),
+			{ minItems: 1 }
+		),
+		websiteId: t.String(),
+		model: t.Optional(
+			t.Union([t.Literal('chat'), t.Literal('agent'), t.Literal('agent-max')])
+		),
+	},
+	{ additionalProperties: false }
+);
 
 export type AssistantRequestType = typeof AssistantRequestSchema.static;

Also applies to: 16-16

apps/api/src/agent/index.ts (1)

8-11: Add backward-compatibility alias for renamed export

Renaming the public export is a breaking change for consumers. Preserve an alias temporarily.

Apply this diff:

 export {
 	AIPlanSchema,
 	AIResponseJsonSchema,
-	comprehensiveSystemPrompt,
+	comprehensiveSystemPrompt,
+	// Temporary compatibility alias; remove in next major
+	comprehensiveSystemPrompt as comprehensiveUnifiedPrompt,
 } from './prompts/agent';
apps/api/src/routes/assistant.ts (1)

28-33: Add a guard for empty messages to fail fast (until schema hardening lands)

Reject requests with no messages array or an empty one before doing further work.

Apply this diff:

-			const { messages, websiteId, model } = body;
+			const { messages, websiteId, model } = body;
+			if (!messages?.length) {
+				return createStreamingResponse(
+					createErrorResponse('At least one message is required')
+				);
+			}
apps/api/src/agent/utils/ai-client.ts (1)

41-45: Sanitize incoming roles before sending to the model (prevent client-supplied system/tool messages).

You're directly spreading request.messages into the messages array. A client could inject system/tool roles and override guardrails. Filter strictly to user/assistant and map to role/content only.

Apply this diff:

-      messages: [
-        { role: 'system', content: systemPrompt },
-        ...request.messages,
-      ],
+      messages: [
+        { role: 'system', content: systemPrompt },
+        ...(Array.isArray(request.messages)
+          ? request.messages
+              .filter((m) => m.role === 'user' || m.role === 'assistant')
+              .map((m) => ({ role: m.role, content: m.content }))
+          : []),
+      ],

Optionally, validate/limit message count and size upstream to avoid token bloat.

apps/api/src/agent/prompts/agent.ts (8)

173-175: Replace <user_query> with “latest user message” to match the new messages-based flow.

The prompt still references <user_query>. Update wording to avoid stale placeholders.

-Your task is to process the <user_query> according to the current <mode>, while strictly adhering to the <core_directives>. When in 'execute_chat' mode, you must consult the extensive <knowledge_base> to construct your response.
+Your task is to process the latest user message in the provided conversation according to the current <mode>, while strictly adhering to the <core_directives>. When in 'execute_chat' mode, you must consult the extensive <knowledge_base> to construct your response.

204-215: Fix ClickHouse INTERVAL syntax in time rules and guidance.

Use INTERVAL 7 DAY (no quotes). The current examples use Postgres-style literals and will mislead the model.

-      - For "this week" or "last 7 days": use time >= today() - INTERVAL '7' DAY
+      - For "this week" or "last 7 days": use time >= today() - INTERVAL 7 DAY
-      - For "last 30 days": use time >= today() - INTERVAL '30' DAY
+      - For "last 30 days": use time >= today() - INTERVAL 30 DAY
-      - Always use proper ClickHouse date functions: today(), yesterday(), date_trunc(), INTERVAL syntax.
+      - Always use proper ClickHouse date functions: today(), yesterday(), date_trunc(), and correct INTERVAL syntax (no quotes).

291-291: Correct INTERVAL syntax in “Simple Time Series” example.

-      <sql>SELECT toDate(time) as date, COUNT(*) AS views FROM analytics.events WHERE client_id = '${websiteId}' AND event_name = 'screen_view' AND time >= today() - INTERVAL '7' DAY GROUP BY date ORDER BY date</sql>
+      <sql>SELECT toDate(time) as date, COUNT(*) AS views FROM analytics.events WHERE client_id = '${websiteId}' AND event_name = 'screen_view' AND time >= today() - INTERVAL 7 DAY GROUP BY date ORDER BY date</sql>

446-447: Correct INTERVAL syntax in “Error Trends Over Time”.

-        <sql>SELECT toDate(timestamp) as date, COUNT(*) as error_count FROM analytics.errors WHERE client_id = '${websiteId}' AND timestamp >= today() - INTERVAL '30' DAY GROUP BY date ORDER BY date</sql>
+        <sql>SELECT toDate(timestamp) as date, COUNT(*) as error_count FROM analytics.errors WHERE client_id = '${websiteId}' AND timestamp >= today() - INTERVAL 30 DAY GROUP BY date ORDER BY date</sql>

575-581: Fix INTERVAL syntax in metric examples (bounce rate and session duration).

-        <sql>WITH session_metrics AS (SELECT session_id, countIf(event_name = 'screen_view') as page_count FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL '7' DAY GROUP BY session_id) SELECT (countIf(page_count = 1) / count()) * 100 AS bounce_rate FROM session_metrics</sql>
+        <sql>WITH session_metrics AS (SELECT session_id, countIf(event_name = 'screen_view') as page_count FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL 7 DAY GROUP BY session_id) SELECT (countIf(page_count = 1) / count()) * 100 AS bounce_rate FROM session_metrics</sql>
-          <sql>WITH session_durations AS (SELECT session_id, dateDiff('second', MIN(time), MAX(time)) as duration FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL '7' DAY GROUP BY session_id HAVING duration > 0) SELECT AVG(duration) AS avg_duration_seconds FROM session_durations</sql>
+          <sql>WITH session_durations AS (SELECT session_id, dateDiff('second', MIN(time), MAX(time)) as duration FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL 7 DAY GROUP BY session_id HAVING duration > 0) SELECT AVG(duration) AS avg_duration_seconds FROM session_durations</sql>

595-596: Fix INTERVAL syntax in “Error Rate” metric example.

-          <sql>WITH total_sessions AS (SELECT uniq(session_id) as total FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL '7' DAY), error_sessions AS (SELECT uniq(session_id) as error_count FROM analytics.errors WHERE client_id = '${websiteId}' AND timestamp >= today() - INTERVAL '7' DAY) SELECT (error_count / total) * 100 AS error_rate FROM error_sessions CROSS JOIN total_sessions</sql>
+          <sql>WITH total_sessions AS (SELECT uniq(session_id) as total FROM analytics.events WHERE client_id = '${websiteId}' AND time >= today() - INTERVAL 7 DAY), error_sessions AS (SELECT uniq(session_id) as error_count FROM analytics.errors WHERE client_id = '${websiteId}' AND timestamp >= today() - INTERVAL 7 DAY) SELECT (error_count / total) * 100 AS error_rate FROM error_sessions CROSS JOIN total_sessions</sql>

475-476: Replace QUALIFY with ClickHouse-compatible approach; simplify entry-page derivation.

ClickHouse doesn't support QUALIFY. Use argMin() to get the first path by time per session.

-      <sql>WITH entry_pages AS (SELECT session_id, path, MIN(time) as entry_time FROM analytics.events WHERE client_id = '${websiteId}' AND event_name = 'screen_view' GROUP BY session_id, path QUALIFY ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY entry_time) = 1), session_metrics AS (SELECT ep.path, ep.session_id, countIf(e.event_name = 'screen_view') as page_count FROM entry_pages ep LEFT JOIN analytics.events e ON ep.session_id = e.session_id WHERE e.client_id = '${websiteId}' GROUP BY ep.path, ep.session_id) SELECT path, COUNT(*) as sessions, (countIf(page_count = 1) / count()) * 100 as bounce_rate FROM session_metrics GROUP BY path HAVING sessions >= 10 ORDER BY bounce_rate DESC LIMIT 15</sql>
+      <sql>WITH entry_pages AS (
+        SELECT session_id, argMin(path, time) AS path
+        FROM analytics.events
+        WHERE client_id = '${websiteId}' AND event_name = 'screen_view'
+        GROUP BY session_id
+      ),
+      session_metrics AS (
+        SELECT ep.path, ep.session_id, countIf(e.event_name = 'screen_view') AS page_count
+        FROM entry_pages ep
+        LEFT JOIN analytics.events e ON ep.session_id = e.session_id
+        WHERE e.client_id = '${websiteId}'
+        GROUP BY ep.path, ep.session_id
+      )
+      SELECT path, COUNT(*) AS sessions, (countIf(page_count = 1) / count()) * 100 AS bounce_rate
+      FROM session_metrics
+      GROUP BY path
+      HAVING sessions >= 10
+      ORDER BY bounce_rate DESC
+      LIMIT 15</sql>

503-504: Remove QUALIFY in “Performance Correlation” subquery; use argMin() per session.

-          SELECT session_id, path, MIN(time) as entry_time 
-          FROM analytics.events 
-          WHERE client_id = '${websiteId}' AND event_name = 'screen_view' 
-          GROUP BY session_id, path 
-          QUALIFY ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY entry_time) = 1
+          SELECT session_id, argMin(path, time) as path
+          FROM analytics.events 
+          WHERE client_id = '${websiteId}' AND event_name = 'screen_view' 
+          GROUP BY session_id
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7e92487 and 77bd3ab.

📒 Files selected for processing (8)
  • apps/api/src/agent/handlers/chart-handler.ts (1 hunks)
  • apps/api/src/agent/index.ts (1 hunks)
  • apps/api/src/agent/processor.ts (3 hunks)
  • apps/api/src/agent/prompts/agent.ts (2 hunks)
  • apps/api/src/agent/utils/ai-client.ts (2 hunks)
  • apps/api/src/routes/assistant.ts (2 hunks)
  • apps/api/src/schemas/assistant-schemas.ts (1 hunks)
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts,jsx,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't use global eval().
Don't use var.
Don't use console.
Don't use debugger.
Don't assign directly to document.cookie.
Don't use duplicate case labels.
Don't use duplicate class members.
Don't use duplicate function parameter names.
Don't use empty block statements and static blocks.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Use for...of statements instead of Array.forEach.
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables.
Don't use constant expressions in conditions.
Don't use Math.min and Math.max to clamp values when the result is constant.
Don't return a value from a constructor.
Don't use empty character classes in regular expression literals.
Don't use empty destructuring patterns.
Don't call global object properties as functions.
Don't declare functions and vars that are accessible outside their block.
Don't use variables and function parameters before they're declared.
Don't use 8 and 9 escape sequences in string literals.
Don't use literal numbers that lose precision.
Don't use duplicate conditions in if-else-if chains.
Don't use two keys with the same name inside objects...

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use TypeScript namespaces.
Don't use the TypeScript directive @ts-ignore.
Don't use any type.
Don't use implicit any type on variable declarations.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't declare empty interfaces.
Use export type for types.
Use import type for types.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.

**/*.{ts,tsx}: Ensure TypeScript type-safety; avoid any unless absolutely necessary
Create proper interfaces/types for APIs, responses, and components; place them in shared types folders rather than in the same file

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{js,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,ts}: Make sure to use the "use strict" directive in script files.
Don't have redundant "use strict".
Make sure to use the "use strict" directive in script files.

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use bun <file> instead of node <file> or ts-node <file>
Do not use dotenv; Bun automatically loads .env files
Do not use express; use Bun.serve() for HTTP servers
Do not use better-sqlite3; use bun:sqlite for SQLite
Do not use ioredis; use Bun.redis for Redis
Do not use pg or postgres.js; use Bun.sql for Postgres
Do not use ws; use built-in WebSocket
Prefer Bun.file over node:fs's readFile/writeFile
Use Bun.$ instead of execa for running shell commands

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{html,ts,css}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/01-MUST-DO.mdc)

**/*.{ts,tsx,js,jsx}: Use console methods appropriately (e.g., console.error, console.time, console.table, console.json)
Use Dayjs instead of date-fns
Use TanStack Query for hooks; do not use SWR
When adding debugging, use JSON.stringify() for structured output

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
**/*.{tsx,jsx,ts,js}

📄 CodeRabbit Inference Engine (.cursor/rules/01-MUST-DO.mdc)

When using Phosphor React icons, append 'Icon' to component names (e.g., CaretIcon, not Caret)

Files:

  • apps/api/src/agent/handlers/chart-handler.ts
  • apps/api/src/agent/utils/ai-client.ts
  • apps/api/src/agent/index.ts
  • apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts
  • apps/api/src/agent/prompts/agent.ts
  • apps/api/src/routes/assistant.ts
  • apps/api/src/schemas/assistant-schemas.ts
  • apps/api/src/agent/processor.ts
🧬 Code Graph Analysis (4)
apps/api/src/agent/utils/ai-client.ts (3)
apps/api/src/agent/index.ts (2)
  • AssistantRequest (5-5)
  • comprehensiveSystemPrompt (10-10)
apps/api/src/agent/processor.ts (1)
  • AssistantRequest (26-28)
apps/api/src/agent/prompts/agent.ts (1)
  • comprehensiveSystemPrompt (45-636)
apps/api/src/agent/prompts/agent.ts (2)
apps/api/src/agent/index.ts (1)
  • comprehensiveSystemPrompt (10-10)
apps/api/src/schemas/assistant-schemas.ts (1)
  • AssistantRequestType (16-16)
apps/api/src/routes/assistant.ts (1)
apps/api/src/lib/website-utils.ts (1)
  • validateWebsite (196-206)
apps/api/src/agent/processor.ts (3)
apps/api/src/agent/index.ts (2)
  • AssistantRequest (5-5)
  • getAICompletion (12-12)
apps/api/src/schemas/assistant-schemas.ts (1)
  • AssistantRequestType (16-16)
apps/api/src/agent/utils/ai-client.ts (1)
  • getAICompletion (26-74)
🔇 Additional comments (3)
apps/api/src/agent/utils/ai-client.ts (1)

41-45: Schema Validates Incoming Roles; System Role Only Injected Internally

The AssistantRequestSchema in assistant-schemas.ts enforces
messages: t.Array with
role: t.Union([t.Literal('user'), t.Literal('assistant')])

Incoming payloads cannot include a system role. In ai-client.ts we intentionally inject

{ role: 'system', content: systemPrompt }

—this is the single authorized source of a system message. A search of routes/assistant.ts shows no other remapping or injection of roles.

No changes needed here.

apps/api/src/agent/processor.ts (2)

59-60: Good: AI call timing isolated and captured.

Measuring AI latency (aiTime) separately from overall processing is helpful for SLOs and optimization.


103-118: Ensure a default chart_type of 'bar' before invoking the chart handler

To prevent UI errors when the AI response omits chart_type, apply a nullish fallback inline—unless handleChartResponse already handles this internally:

-          yield* handleChartResponse(parsedResponse, {
+          yield* handleChartResponse(
+            { ...parsedResponse, chart_type: parsedResponse.chart_type ?? 'bar' },
+            {
               ...context,
               startTime,
               aiTime,
-          });
+            }
+          );

Please confirm whether handleChartResponse already defaults chart_type to 'bar'. If it does, you can omit this inline fallback to keep a single source of truth.

Comment thread apps/api/src/agent/processor.ts
Comment thread apps/api/src/agent/utils/ai-client.ts
Comment thread apps/api/src/agent/utils/ai-client.ts
Comment on lines 27 to 33
async ({ body, user }: { body: AssistantRequestType; user: User }) => {
const { message, website_id, model, context } = body;
const { messages, websiteId, model } = body;

try {
const websiteValidation = await validateWebsite(website_id);
const websiteValidation = await validateWebsite(websiteId);

if (!websiteValidation.success) {

@coderabbitai coderabbitai Bot Aug 16, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Ensure websiteId in body matches authenticated header to prevent cross-website access

The route validates the website using the body.websiteId while auth likely relies on the X-Website-Id header. If these diverge and aren’t checked, a client could authenticate with one site and attempt to query another. Enforce equality.

Apply this diff:

-		async ({ body, user }: { body: AssistantRequestType; user: User }) => {
+		async ({
+			body,
+			user,
+			headers,
+		}: {
+			body: AssistantRequestType;
+			user: User;
+			headers: Record<string, string>;
+		}) => {
 			const { messages, websiteId, model } = body;
+			const headerWebsiteId = headers['x-website-id'];
+			if (headerWebsiteId && headerWebsiteId !== websiteId) {
+				return createStreamingResponse(
+					createErrorResponse('websiteId mismatch between header and body')
+				);
+			}

If websiteAuth already injects a canonical websiteId elsewhere, alternatively drop websiteId from the body and use the authenticated one everywhere.

📝 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
async ({ body, user }: { body: AssistantRequestType; user: User }) => {
const { message, website_id, model, context } = body;
const { messages, websiteId, model } = body;
try {
const websiteValidation = await validateWebsite(website_id);
const websiteValidation = await validateWebsite(websiteId);
if (!websiteValidation.success) {
async ({
body,
user,
headers,
}: {
body: AssistantRequestType;
user: User;
headers: Record<string, string>;
}) => {
const { messages, websiteId, model } = body;
const headerWebsiteId = headers['x-website-id'];
if (headerWebsiteId && headerWebsiteId !== websiteId) {
return createStreamingResponse(
createErrorResponse('websiteId mismatch between header and body')
);
}
try {
const websiteValidation = await validateWebsite(websiteId);
if (!websiteValidation.success) {
// …
🤖 Prompt for AI Agents
In apps/api/src/routes/assistant.ts around lines 27 to 33, the handler currently
validates websiteId from the request body but does not ensure it matches the
authenticated website id supplied via headers/middleware; enforce equality by
comparing body.websiteId to the authenticated website id (e.g., req.header or
user.websiteId injected by websiteAuth) and return a 403/validation error if
they differ, or remove/ignore websiteId from the body and always use the
canonical authenticated website id throughout the route (update subsequent calls
to validateWebsite/use websiteId accordingly).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We should add this, isn't it? @izadoesdev

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread apps/dashboard/app/(main)/websites/[id]/assistant/hooks/use-chat.ts Outdated
@izadoesdev

Copy link
Copy Markdown
Member

now watch me merge... now watch me nae nae
giphy (1)

@sbansal1999

Copy link
Copy Markdown
Contributor Author

let me fix the comments!

@sbansal1999
sbansal1999 force-pushed the ai-flow-simplifcation branch 2 times, most recently from db0a8bf to 03fd9e8 Compare August 17, 2025 07:20
@sbansal1999
sbansal1999 force-pushed the ai-flow-simplifcation branch from 03fd9e8 to a51751a Compare August 17, 2025 08:21
@sbansal1999

sbansal1999 commented Aug 17, 2025

Copy link
Copy Markdown
Contributor Author

addressed all most of the feedback

@izadoesdev
izadoesdev merged commit 7e322ac into databuddy-analytics:staging Aug 17, 2025
2 of 4 checks passed
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