feat: add insights chat box with groq integration#28422
feat: add insights chat box with groq integration#28422
Conversation
- Add fixed-position chat box on /insights page - Use Groq (llama-3.3-70b-versatile) to interpret natural language queries - Server-side data fetching via InsightsBookingBaseService - Dynamic chart rendering (line, bar, pie, area) with recharts - Save/remove charts via localStorage persistence - Support 14 different insights endpoints - Add i18n translation keys Co-Authored-By: peer@cal.com <peer@cal.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
7 issues found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/modules/insights/components/chat/InsightsChatChart.tsx">
<violation number="1" location="apps/web/modules/insights/components/chat/InsightsChatChart.tsx:105">
P2: Pie charts ignore the `colors` returned in `ChatChartResult`, so this chart type renders with a different palette than the API requested.</violation>
<violation number="2" location="apps/web/modules/insights/components/chat/InsightsChatChart.tsx:188">
P3: Localize the Save button label with `t(...)` instead of hard-coded English text.</violation>
</file>
<file name="apps/web/modules/insights/views/insights-view.tsx">
<violation number="1" location="apps/web/modules/insights/views/insights-view.tsx:162">
P1: Wire the chat box to the current insights scope before mounting it. Right now org/team views still query `scope: "user"`, so the chat can return data that doesn't match the visible insights filters.</violation>
</file>
<file name="apps/web/modules/insights/components/chat/SavedCharts.tsx">
<violation number="1" location="apps/web/modules/insights/components/chat/SavedCharts.tsx:14">
P2: Validate the parsed localStorage value before treating it as a chart array.</violation>
<violation number="2" location="apps/web/modules/insights/components/chat/SavedCharts.tsx:45">
P3: Localize these new UI labels instead of hard-coding English strings.</violation>
</file>
<file name="apps/web/app/api/insights/chat/route.ts">
<violation number="1" location="apps/web/app/api/insights/chat/route.ts:235">
P2: Unsafe `JSON.parse` on LLM output with no schema validation. If Groq returns malformed JSON, markdown-wrapped JSON, or a valid JSON object missing expected keys, this will either throw a parse error or silently produce `undefined` fields. Use a Zod schema with `.safeParse()` to validate the parsed shape and provide a clear fallback or retry.</violation>
<violation number="2" location="apps/web/app/api/insights/chat/route.ts:440">
P0: Authorization bypass: this route only checks for a valid session but never verifies `insights.read` permission or team membership. All tRPC insights endpoints use `userBelongsToTeamProcedure` (which calls `PermissionCheckService.checkPermission`) — this REST route bypasses that entirely. Any authenticated user can pass an arbitrary `selectedTeamId` and `scope` to read another team's booking data.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
apps/web/modules/insights/components/chat/InsightsChatChart.tsx
Outdated
Show resolved
Hide resolved
| try { | ||
| const stored = localStorage.getItem(STORAGE_KEY); | ||
| if (!stored) return []; | ||
| return JSON.parse(stored) as ChatChartResult[]; |
There was a problem hiding this comment.
P2: Validate the parsed localStorage value before treating it as a chart array.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/modules/insights/components/chat/SavedCharts.tsx, line 14:
<comment>Validate the parsed localStorage value before treating it as a chart array.</comment>
<file context>
@@ -0,0 +1,61 @@
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (!stored) return [];
+ return JSON.parse(stored) as ChatChartResult[];
+ } catch {
+ return [];
</file context>
apps/web/modules/insights/components/chat/InsightsChatChart.tsx
Outdated
Show resolved
Hide resolved
Devin AI is addressing Cubic AI's review feedbackNew feedback has been sent to the existing Devin session. ✅ Pushed commit |
- Add authorization checks (team membership + insights.read permission) - Add Zod schema validation for Groq LLM JSON output - Wire chat box to current insights scope (user/team/org) - Fix pie chart to use result.colors from API response - Localize Save/Saved button labels with t() - Localize Saved Charts heading and Remove button with t() - Add saved_charts i18n key Co-Authored-By: peer@cal.com <peer@cal.com>
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/modules/insights/components/chat/InsightsChatBox.tsx">
<violation number="1" location="apps/web/modules/insights/components/chat/InsightsChatBox.tsx:59">
P1: Include `scope` and `selectedTeamId` in the submit callback dependencies; otherwise requests can be sent with stale filters after the parent props change.</violation>
</file>
<file name="apps/web/app/api/insights/chat/route.ts">
<violation number="1" location="apps/web/app/api/insights/chat/route.ts:244">
P2: `JSON.parse()` can throw a `SyntaxError` before `safeParse` runs, defeating the purpose of using Zod validation for the LLM response. Wrap the parse in a try-catch to produce a clear error when the LLM returns malformed JSON.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Devin AI is addressing Cubic AI's review feedbackNew feedback has been sent to the existing Devin session. ✅ Pushed commit |
- Add scope and selectedTeamId to handleSubmit useCallback dependencies - Wrap JSON.parse in try-catch before Zod safeParse for LLM output Co-Authored-By: peer@cal.com <peer@cal.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/app/api/insights/chat/route.ts">
<violation number="1" location="apps/web/app/api/insights/chat/route.ts:248">
P2: Do not include raw Groq output in the parse error message; it gets logged by the route catch block.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Devin AI is addressing Cubic AI's review feedbackNew feedback has been sent to the existing Devin session. ✅ Pushed commit |
Co-Authored-By: peer@cal.com <peer@cal.com>
What does this PR do?
Adds a fixed-position chat box at the bottom of the
/insightspage that accepts natural language queries about booking data. Uses Groq (llama-3.3-70b-versatile) to interpret queries, maps them to existing insights data endpoints server-side, and dynamically renders the best chart type (line, bar, pie, area) using recharts. Users can save generated charts to localStorage.Architecture:
apps/web/app/api/insights/chat/route.ts): Receives natural language query → calls Groq to determine endpoint + chart type → fetches data server-side viaInsightsBookingBaseService→ returns chart config + dataInsightsChatBox.tsx): Pure presentation layer — sends query to API, renders resultsNo Zod validation on Groq LLM JSON output— Fixed: AddedGroqParsedSchemaZod schema with.safeParse(), plus try-catch aroundJSON.parsefor malformed JSONScope hardcoded to— Fixed: Chat box now receives"user"scopeandselectedTeamIdfromuseInsightsOrgTeamshook via propsweekStartnow reads from user preferences (user.weekStart) instead of being hardcodedNo authorization beyond session check— Fixed: AddedPermissionCheckService.checkPermissionforinsights.readand team membership verification matching the tRPC patternGROQ_API_KEYenvironment variable required — runtime check only, no startup validationRecord<string, unknown>[]is used throughout as the chart data type — loose typingSuggested human review checklist
route.ts:484-523correctly mirrors the tRPCuserBelongsToTeamProcedurepattern — custom auth in API routes is inherently riskier than reusing middlewarePermissionCheckService.checkPermissionAPI matches what's used in the codebase (params:userId,teamId,permission,fallbackRoles)scopefromuseInsightsOrgTeamscould be undefined or mismatched withselectedTeamIdgetSavedCharts()handles corrupt/unexpected data gracefully (currently uses try-catch but no schema validation on the parsed array)Updates since last revision
Addressed Cubic AI review feedback (9 issues with confidence ≥ 9/10 across 3 review rounds):
PermissionCheckServicechecks for team membership andinsights.readpermission in the API route POST handlerGroqParsedSchemawith.safeParse()and wrappedJSON.parsein try-catch for malformed LLM outputInsightsChatBoxnow acceptsscopeandselectedTeamIdprops fromuseInsightsOrgTeamsresult.colors[index]with CHART_COLORS fallbackt()with existing i18n keys (save,saved,remove,saved_charts)scopeandselectedTeamIdtohandleSubmitdependency arraySkipped: localStorage validation (confidence 8/10, below threshold).
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
GROQ_API_KEYenvironment variable with a valid Groq API key/insightspage (logged in as a user with booking data)Checklist
Link to Devin Session: https://app.devin.ai/sessions/bf30df83b3c34a5eb14d101f3f92eab9
Requested by: @PeerRich