Conversation
…ing in client side
python backend only used for model related tasks simplify auth and validation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughSummary by CodeRabbit
WalkthroughRefactors chat from a client/FastAPI-driven flow to Next.js App Router with Prisma-backed server actions, adds new composable chat UI components, replaces backend chat routes with model-generation endpoints, and centralizes ingestion/generation responsibilities to the backend API. Changes
Sequence DiagramsequenceDiagram
participant User as User Browser
participant NextApp as Next.js App
participant Prisma as Prisma DB
participant Backend as Backend API
participant Vector as Vector Service
User->>NextApp: Request /chat or /chat/{id}
NextApp->>Prisma: getUserChats / getChatById (session user)
Prisma-->>NextApp: chats / chat data
NextApp-->>User: render ChatLayout (or redirect to /login)
User->>NextApp: send message (client action)
NextApp->>Prisma: persist user message (server action)
Prisma-->>NextApp: ack
NextApp->>Backend: POST /api/v1/model/generate {chat_id, query}
Backend->>Vector: search(collection=chat_id, query)
Vector-->>Backend: documents
Backend->>Backend: generate answer (RAG)
Backend-->>NextApp: assistant message
NextApp->>Prisma: persist assistant message, update chat.updatedAt
Prisma-->>NextApp: ack
NextApp-->>User: deliver assistant message (via page update)
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment Tip CodeRabbit can use TruffleHog to scan for secrets in your code with verification capabilities.Add a TruffleHog config file (e.g. trufflehog-config.yml, trufflehog.yml) to your project to customize detectors and scanning behavior. The tool runs only when a config file is present. |
There was a problem hiding this comment.
Pull request overview
This PR refactors the app to use Better Auth + Prisma-backed persistence for chats/messages, updates the Next.js chat UI and auth flows, and adjusts the FastAPI backend to expose a model generation endpoint (instead of owning chat persistence).
Changes:
- Migrate chat history persistence into Next.js/Prisma (new Prisma client, chat data access, server actions, and
/chatrouting/layout). - Replace server-action auth handlers with Better Auth client usage + add confirm-password validation.
- Update FastAPI routing to add
/api/v1/model/*endpoints and adjust generation/checkpoint loading.
Reviewed changes
Copilot reviewed 72 out of 75 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| server/users.ts | Removed old server-action based signin/signup handlers. |
| proxy.ts | Middleware auth gating for /chat/*, /login, /signup. |
| prisma/schema.prisma | Adds Better Auth models + user-owned chats/messages. |
| prisma.config.ts | Removes explicit Prisma engine setting. |
| package.json | Dependency tweaks (Radix/Prisma) and adds separator. |
| lib/validation/auth.ts | Adds confirmPassword + splits server/client signup schemas. |
| lib/utils.ts | Formatting/semicolons. |
| lib/types.ts | Introduces shared Chat/Message TS types. |
| lib/prisma.ts | Adds PrismaClient singleton. |
| lib/data/chat.ts | Adds Prisma chat queries for sidebar + chat detail pages. |
| lib/auth.ts | Switches Better Auth DB integration to Prisma adapter + request validation hooks. |
| lib/auth-client.ts | Updates auth client usage and callback URL. |
| lib/actions/chat.ts | Adds server actions for chat CRUD, messaging, and document upload. |
| hooks/use-mobile.ts | Formatting/semicolons. |
| docs/RAG_PIPELINE.md | Markdown formatting cleanup. |
| docs/API_REFERENCE.md | Markdown formatting cleanup. |
| components/ui/tooltip.tsx | Formatting/semicolons. |
| components/ui/textarea.tsx | Formatting/semicolons. |
| components/ui/sonner.tsx | Formatting/semicolons. |
| components/ui/skeleton.tsx | Formatting/semicolons. |
| components/ui/sidebar.tsx | Sidebar component refactor + RTL/layout tweaks. |
| components/ui/sheet.tsx | Formatting/semicolons. |
| components/ui/separator.tsx | Separator component refactor. |
| components/ui/scroll-area.tsx | Formatting/semicolons. |
| components/ui/resizable.tsx | Formatting/semicolons/indentation. |
| components/ui/label.tsx | Formatting/semicolons. |
| components/ui/input.tsx | Formatting/semicolons. |
| components/ui/field.tsx | Formatting/semicolons + minor structure updates. |
| components/ui/dropdown-menu.tsx | Formatting/semicolons. |
| components/ui/dialog.tsx | Dialog component refactor. |
| components/ui/card.tsx | Formatting/semicolons. |
| components/ui/button.tsx | Formatting/semicolons. |
| components/ui/avatar.tsx | Adds avatar UI component. |
| components/tooltip.tsx | Formatting/semicolons/quotes. |
| components/theme-provider.tsx | Formatting/semicolons/quotes. |
| components/textarea.tsx | Formatting/semicolons/quotes. |
| components/skeleton.tsx | Formatting/semicolons/quotes. |
| components/sheet.tsx | Formatting/semicolons/quotes. |
| components/separator.tsx | Formatting/semicolons/quotes. |
| components/scroll-area.tsx | Formatting/semicolons/quotes. |
| components/mode-toggle.tsx | Formatting + wraps button props across lines. |
| components/label.tsx | Formatting/semicolons/quotes. |
| components/dropdown-menu.tsx | Formatting/semicolons/quotes. |
| components/chat/chat.tsx | New chat layout container component. |
| components/chat/chat-toolbar.tsx | New chat toolbar primitives. |
| components/chat/chat-messages.tsx | New message list container primitive. |
| components/chat/chat-header.tsx | New chat header primitives. |
| components/chat/chat-event.tsx | New message row primitives + timestamp formatting. |
| components/chat-sidebar.tsx | Sidebar updated to use server actions + session display. |
| components/chat-interface.tsx | Removes old client-only chat interface implementation. |
| components/chat-area.tsx | Chat area updated to use new primitives + server actions. |
| components/auth-form.tsx | Auth flow updated to Better Auth client; adds Google button + password visibility. |
| checkpoint-8142/trainer_state.json | Adds/updates training checkpoint artifact. |
| checkpoint-8142/config.json | Adds/updates training checkpoint config. |
| backend/services/generation_service.py | Uses repo-relative checkpoint path. |
| backend/main.py | Replaces chat router with model router; removes SQL bootstrap. |
| backend/core/database.py | Makes generation model lazy-init to reduce startup RAM. |
| backend/api/v1/model_routes.py | New model endpoints: generate + delete vector collection. |
| backend/api/v1/ingestion_routes.py | Removes SQL chat verification/count updates (now handled by Next.js). |
| backend/api/v1/chat_routes.py | Removes legacy FastAPI chat persistence endpoints. |
| app/signup/page.tsx | Updates signup page layout and uses default AuthForm fields. |
| app/signup/loading.tsx | Adds signup route loading skeleton. |
| app/page.tsx | Minor formatting. |
| app/login/page.tsx | Updates login page layout and uses default AuthForm fields. |
| app/login/loading.tsx | Adds login route loading skeleton. |
| app/layout.tsx | Formatting/semicolons; ThemeProvider props expanded. |
| app/globals.css | Formatting for html/body selector. |
| app/dashboard/page.tsx | Formatting/semicolons. |
| app/chat/page.tsx | Server-side session gate + redirect to first chat. |
| app/chat/loading.tsx | Adds chat loading skeleton. |
| app/chat/layout.tsx | Adds server-side chat layout fetching sidebar chats. |
| app/chat/[chatId]/page.tsx | Adds chat detail page loading chat + messages server-side. |
| app/api/auth/[...all]/route.ts | Minor formatting. |
| README.md | Updates docs to reflect current setup/stack wording and formatting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
README.md (1)
45-49:⚠️ Potential issue | 🟠 MajorResolve PostgreSQL vs Supabase setup inconsistency.
Line 45 says to use a Supabase connection string, but Line 41 now documents generic PostgreSQL. This contradiction can break setup for local/self-hosted users.
Suggested fix
-Copy the provided `.env.example` to create `.env` files in both the root directory and `backend/` directory, and ensure the **DATABASE_URL** is set to your Supabase connection string: +Copy the provided `.env.example` to create `.env` files in both the root directory and `backend/` directory, and ensure **DATABASE_URL** points to your PostgreSQL instance: ```properties -DATABASE_URL="postgresql://postgres:<YOUR_PASSWORD>@db.<YOUR_SUPABASE_REF>.supabase.co:5432/postgres" +DATABASE_URL="postgresql://<USER>:<PASSWORD>@<HOST>:5432/<DATABASE_NAME>"</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@README.mdaround lines 45 - 49, The README currently mixes a
Supabase-specific DATABASE_URL example with a generic PostgreSQL instruction;
update the .env example and text so the DATABASE_URL shown is a generic Postgres
connection string
(DATABASE_URL="postgresql://:@:5432/<DATABASE_NAME>") and
add a brief note that Supabase users should replace with the Supabase DB
host (db.<YOUR_SUPABASE_REF>.supabase.co) and <DATABASE_NAME> with "postgres";
ensure this change applies to both the root and backend .env instructions and
any surrounding text that references Supabase vs PostgreSQL.</details> </blockquote></details> <details> <summary>components/ui/label.tsx (1)</summary><blockquote> `1-24`: _⚠️ Potential issue_ | _🟡 Minor_ **Remove unused duplicate component at `components/label.tsx`.** This file is identical to `components/ui/label.tsx` but is not imported anywhere in the codebase. Only `components/ui/label` is actively imported (by `components/ui/field.tsx` and `components/chat-sidebar.tsx`). Delete `components/label.tsx` to eliminate dead code and reduce maintenance burden. <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@components/ui/label.tsxaround lines 1 - 24, There is a duplicate, unused
Label component implementation that matches the exported Label in
components/ui/label.tsx; remove the dead file components/label.tsx to avoid
maintenance overhead and potential confusion, and verify that all imports (e.g.,
any references to Label) use components/ui/label so no import paths break after
deletion; if any stray import points to components/label.tsx, update them to
import the Label from components/ui/label before deleting the duplicate.</details> </blockquote></details> <details> <summary>backend/api/v1/ingestion_routes.py (1)</summary><blockquote> `68-84`: _⚠️ Potential issue_ | _🟠 Major_ **Keep `documentCount` mutation backend-owned to avoid state drift.** With this change, ingestion can succeed while SQL count update is skipped/failed outside this API path, causing persistent inconsistency (especially for non-Next.js callers). This endpoint should update count transactionally (or emit a durable backend event) in the same backend workflow. Also, the function docstring still states SQL updates happen here. Also applies to: 140-141 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@backend/api/v1/ingestion_routes.pyaround lines 68 - 84, The ingest_document
endpoint currently leaves chat document count mutation to external callers which
risks state drift; update the ingest_document implementation (the
ingest_document function and related IngestionService logic) so the SQL chat
document count is updated transactionally as part of the same backend workflow
(or emit a durable backend event from IngestionService on success) and ensure
the docstring reflects this behavior; locate where IngestionService.ingest (or
similar) stores embeddings and add a DB transaction or reliable event emission
to increment the chat's documentCount within that same operation, and update the
ingest_document docstring wording that claims the SQL update happens here.</details> </blockquote></details> <details> <summary>components/ui/sidebar.tsx (1)</summary><blockquote> `154-166`: _⚠️ Potential issue_ | _🟠 Major_ **Keep `Sidebar`'s public `div` props consistent across breakpoints.** `Sidebar` is typed as `React.ComponentProps<"div">`, but in the mobile branch those props are spread onto `<Sheet>` instead of the rendered panel, so `className` is dropped there and other DOM props no longer land on the actual sidebar element. At the same time, `dir` is destructured and only forwarded on mobile, so desktop never gets component-scoped RTL. <details> <summary>🛠️ One way to make the mobile/desktop contract consistent</summary> ```diff function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, dir, + style, ...props }: React.ComponentProps<"div"> & { side?: "left" | "right"; variant?: "sidebar" | "floating" | "inset"; collapsible?: "offcanvas" | "icon" | "none"; }) { @@ if (isMobile) { return ( - <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> + <Sheet open={openMobile} onOpenChange={setOpenMobile}> <SheetContent + {...props} dir={dir} data-sidebar="sidebar" data-slot="sidebar" data-mobile="true" - className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" + className={cn( + "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden", + className, + )} style={ { "--sidebar-width": SIDEBAR_WIDTH_MOBILE, + ...style, } as React.CSSProperties } side={side} > @@ <div data-slot="sidebar-container" + dir={dir} data-side={side} + style={style} className={cn( "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex data-[side=left]:left-0 data-[side=right]:right-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className, )} {...props} >Also applies to: 184-205, 210-243
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/sidebar.tsx` around lines 154 - 166, The Sidebar component currently types its public API as React.ComponentProps<"div"> but forwards those props inconsistently: props like className and other DOM attributes are spread onto the mobile <Sheet> instead of the actual rendered panel, and dir is only forwarded on mobile; fix this by ensuring all remaining div props (including className, dir and any other DOM props from the rest/spread) are forwarded to the rendered sidebar panel element in both mobile and desktop branches. Concretely, keep the Sidebar signature and rest spread as-is, remove spreading rest directly onto <Sheet>, and instead pass only Sheet-specific props to <Sheet> while forwarding the original rest props (or a filtered subset) to the panel element (the panel component rendered inside <Sheet> and the desktop panel element) so className and dir are applied consistently across breakpoints; use explicit prop names (e.g., className, dir) or a small helper to split sheetProps vs panelProps if needed.lib/validation/auth.ts (1)
74-79:⚠️ Potential issue | 🟠 Major
parseSignupFormData()is out of sync withsignupSchema.After adding
confirmPasswordtosignupSchema, this helper still parses onlyname,password. Any caller using it now gets a validation failure unlessconfirmPasswordis also read here, or the helper switches back toserverSignupSchema.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/validation/auth.ts` around lines 74 - 79, The parseSignupFormData helper is missing the confirmPassword field required by signupSchema; update parseSignupFormData (and any callers relying on it) to read confirmPassword from the FormData and include it in the object passed to signupSchema.safeParse (e.g., add confirmPassword: String(formData.get("confirmPassword") || "")), or alternatively change the helper to use serverSignupSchema if that was intended; ensure the returned safeParse input matches whichever schema (signupSchema or serverSignupSchema) is actually expected.
🟡 Minor comments (15)
docs/RAG_PIPELINE.md-83-83 (1)
83-83:⚠️ Potential issue | 🟡 MinorFix markdownlint MD038 violation in inline code span.
Line 83 has a code span with an internal trailing space (
.), which triggersno-space-in-code. Remove the trailing space inside the backticks.Suggested fix
-- **Separators**: Prioritizes splitting at paragraphs (`\n\n`), then sentences (`. `), then spaces. +- **Separators**: Prioritizes splitting at paragraphs (`\n\n`), then sentences (`.`), then spaces.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/RAG_PIPELINE.md` at line 83, The inline code span in the "Separators" line contains a trailing space inside the backticks (`. `) which triggers markdownlint MD038; edit the text so the code span has no internal trailing space by replacing `. ` with `.` (i.e., use `.` instead of `. `) in that inline code snippet.components/skeleton.tsx-1-13 (1)
1-13:⚠️ Potential issue | 🟡 MinorRemove unused duplicate
Skeletoncomponent atcomponents/skeleton.tsx.This file is identical to
components/ui/skeleton.tsxbut is not imported anywhere in the codebase. All usage is fromcomponents/ui/skeleton.tsx(components/ui/sidebar.tsx, app/signup/loading.tsx, app/login/loading.tsx, app/chat/loading.tsx). Remove the duplicate to avoid maintenance confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/skeleton.tsx` around lines 1 - 13, Remove the duplicate Skeleton component by deleting the redundant function export (Skeleton) in this file; ensure the project uses the single canonical Skeleton implementation (the other Skeleton symbol already used across the codebase) and remove any exports or barrel-export entries that reference this duplicate so nothing imports it; after removing the file, run a repo-wide search and a build/test to confirm no remaining references to this Skeleton remain and update any import paths if necessary.components/ui/field.tsx-194-197 (1)
194-197:⚠️ Potential issue | 🟡 MinorUse a nullish check for
childreninFieldError.Truthy checks skip valid renderable values (e.g.,
0,""). Prefer an explicit nullish guard.Suggested fix
- if (children) { + if (children !== null && children !== undefined) { return children; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/field.tsx` around lines 194 - 197, The current truthy check in the useMemo that builds `content` inside the FieldError/Field component skips valid renderable values like 0 or ""; change the guard from a boolean/truthy test to an explicit nullish check (e.g., use `children != null` or `children !== null && children !== undefined`) in the `useMemo` block where `content` is computed so `0` and empty string render correctly; update the `content` creation in the `useMemo` that references `children` (and any similar checks in the same component) to use this nullish check.components/ui/sidebar.tsx-610-612 (1)
610-612:⚠️ Potential issue | 🟡 MinorReplace
Math.random()with a stable width value in the unusedSidebarMenuSkeletoncomponent.The
SidebarMenuSkeletoncomponent usesMath.random()in a Client Component that will be prerendered on the server. Computing a random width during render would cause the server HTML and client render to disagree, creating a hydration mismatch. Though this component is currently exported but not used in the codebase, if it becomes active, this will cause hydration issues and width flicker.Use a stable width instead
- const width = React.useMemo(() => { - return `${Math.floor(Math.random() * 40) + 50}%`; - }, []); + const width = "70%";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/sidebar.tsx` around lines 610 - 612, The SidebarMenuSkeleton component computes width using Math.random() which will cause SSR/client hydration mismatches; replace the random calculation in the width useMemo with a stable deterministic value (e.g., a fixed percentage string like "60%") or derive it from a deterministic prop, removing Math.random() entirely; update the const width = React.useMemo(...) in SidebarMenuSkeleton so it returns a constant string and ensure any tests or usages reference that stable width.backend/main.py-83-85 (1)
83-85:⚠️ Potential issue | 🟡 MinorUpdate
/infoendpoint metadata after switching tomodel_router.After Line 83 switches registration to
model_router, the/infopayload still exposes old vector/chat-oriented endpoint keys (Lines 135-138), which makes service discovery stale for clients.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/main.py` around lines 83 - 85, The /info endpoint payload was not updated after switching to model_router: update the function that builds the /info response (the handler that returns the info metadata) to replace the old vector/chat-oriented keys with the new model_router-related keys and values (e.g., rename or remove former vector/chat entries and add the model_router endpoints and capabilities), ensuring the returned JSON reflects the current routes registered under model_router and any changed field names.app/chat/loading.tsx-6-6 (1)
6-6:⚠️ Potential issue | 🟡 Minor
w-75is not a valid Tailwind width utility and will not apply.At line 6, the class
w-75is not part of the default Tailwind v4 width scale (which includes 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96). No custom width configuration for "75" exists in the codebase, so the sidebar will have no width constraint on medium+ screens.💡 Suggested fix
- <div className="w-75 shrink-0 border-r border-sidebar-border hidden md:flex flex-col"> + <div className="w-[18.75rem] shrink-0 border-r border-sidebar-border hidden md:flex flex-col">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/chat/loading.tsx` at line 6, The class "w-75" in the div in app/chat/loading.tsx is not a valid Tailwind width utility; replace it with a valid Tailwind width class (for example w-72 or w-80) or an arbitrary value like w-[75%] depending on the intended width so the sidebar actually gets a width on md+ screens; update the class string on the div node that currently contains "w-75 shrink-0 border-r border-sidebar-border hidden md:flex flex-col" accordingly.backend/api/v1/model_routes.py-32-34 (1)
32-34:⚠️ Potential issue | 🟡 MinorUse exception chaining when re-raising.
Per static analysis (B904), use
raise ... from eto preserve the exception chain for better debugging.🔧 Proposed fix
except Exception as e: logger.error(f"Generate error: {e}") - raise HTTPException(status_code=500, detail="Failed to generate answer") + raise HTTPException(status_code=500, detail="Failed to generate answer") from e🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/api/v1/model_routes.py` around lines 32 - 34, The except block in model_routes.py currently logs the exception and re-raises an HTTPException without chaining, losing the original traceback; update the handler so the raised HTTPException is chained from the caught exception (use "raise HTTPException(status_code=500, detail='Failed to generate answer') from e") and keep the existing logger.error(f"Generate error: {e}") call so the original exception context is preserved; locate the except Exception as e block surrounding the generate logic and modify the raise to use "from e".backend/api/v1/model_routes.py-23-29 (1)
23-29:⚠️ Potential issue | 🟡 MinorEdge case: context with only whitespace may pass the truthiness check.
If all documents have empty
textfields,context_strbecomes"\n\n"(or similar whitespace), which is truthy. This would invoke the generation service with an empty context.🔧 Proposed fix to strip and check
- context_str = "\n\n".join([doc.text for doc in search_results.results]) - - if context_str: + context_str = "\n\n".join([doc.text for doc in search_results.results]).strip() + + if context_str:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/api/v1/model_routes.py` around lines 23 - 29, The current truthiness check on context_str can be true if it contains only whitespace (e.g., "\n\n"), causing generation_service.generate_answer to be called with an empty context; change the logic that builds and checks context to trim and validate non-empty document texts (e.g., filter search_results.results for doc.text.strip() truthy before joining) and only call get_generation_service() / generation_service.generate_answer(request.query, context_str) when the resulting context_str after strip/join is non-empty; otherwise set ai_content to the fallback message. Ensure you update references to context_str, search_results.results, get_generation_service, and generation_service.generate_answer in the modified block.backend/api/v1/model_routes.py-39-45 (1)
39-45:⚠️ Potential issue | 🟡 Minor**The
delete_collectionreturn value is ignored.**The search was forbetter-auth delete_collectionbut this is about the vector service, not better-auth. Let me focus on the actual code issue.Per the relevant snippet from
backend/services/vector_service.py,delete_collectionreturns aDeleteCollectionResponsewithsuccessandmessagefields. The route ignores this response and always returns success, even if the service indicates failure.🔧 Proposed fix to use the service response
`@router.delete`("/{chat_id}") def delete_chat_vector_collection(chat_id: str): """Delete the vector collection for a specific chat.""" try: vector_service = get_vector_service() - vector_service.delete_collection(chat_id) - return {"status": "success", "message": "Vector collection deleted"} + result = vector_service.delete_collection(chat_id) + if result.success: + return {"status": "success", "message": result.message} + else: + return {"status": "warning", "message": result.message} except Exception as e: logger.warning(f"Failed to delete vector collection for chat {chat_id}: {e}") return {"status": "warning", "message": "Failed to delete from vector DB, or it didn't exist"}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/api/v1/model_routes.py` around lines 39 - 45, The route currently ignores the DeleteCollectionResponse from vector_service.delete_collection(chat_id) and always returns success; update the code to capture the response (e.g., resp = vector_service.delete_collection(chat_id)), check resp.success, and return {"status":"success","message": resp.message} when true or {"status":"warning","message": resp.message} when false, while logging the failure via logger.warning with the response.message; use get_vector_service() and vector_service.delete_collection(chat_id) as the locating symbols and ensure exceptions are still caught as before.lib/auth.ts-14-19 (1)
14-19:⚠️ Potential issue | 🟡 MinorUnsafe environment variable casting may cause runtime issues.
Casting
process.env.GOOGLE_CLIENT_ID as stringwhen the variable is undefined will pass"undefined"(the string) to the OAuth provider, leading to cryptic authentication failures rather than a clear startup error.🛡️ Proposed fix to validate env vars
+const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID; +const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET; + +if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) { + throw new Error("Missing Google OAuth environment variables"); +} + export const auth = betterAuth({ // ... socialProviders: { google: { - clientId: process.env.GOOGLE_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + clientId: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, }, },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/auth.ts` around lines 14 - 19, The current cast of process.env.GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET to string in the socialProviders.google config is unsafe; instead validate and fail fast: replace the direct casts with a required-env check (e.g., call a small helper like getRequiredEnv('GOOGLE_CLIENT_ID') and getRequiredEnv('GOOGLE_CLIENT_SECRET') or inline if-checks) and throw a clear Error when missing, then use the validated values for socialProviders.google.clientId and .clientSecret so you never pass literal "undefined" to the OAuth provider.app/layout.tsx-8-9 (1)
8-9:⚠️ Potential issue | 🟡 MinorFont variables are declared but not applied.
_geistand_geistMonoare loaded but never used. Thebodyelement usesfont-sansbut doesn't apply the loaded font classes. Either apply the font classes or remove the unused imports to avoid unnecessary font loading.💡 Proposed fix to apply fonts
-const _geist = Geist({ subsets: ["latin"] }); -const _geistMono = Geist_Mono({ subsets: ["latin"] }); +const geist = Geist({ subsets: ["latin"], variable: "--font-geist" }); +const geistMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" }); // ... - <body className={`font-sans antialiased`}> + <body className={`${geist.variable} ${geistMono.variable} font-sans antialiased`}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/layout.tsx` around lines 8 - 9, The _geist and _geistMono font variables are imported/created but never applied; update the root layout so the body element includes the generated classNames from _geist and/or _geistMono (e.g., combine _geist.className and _geistMono.className with existing "font-sans") to actually apply the fonts, or if you don't intend to use them remove the Geist(...) and Geist_Mono(...) calls to avoid loading unused fonts; specifically modify the element that renders the body (where "font-sans" is used) to include the font variables (_geist.className and/or _geistMono.className) or delete the unused _geist/_geistMono declarations.lib/data/chat.ts-19-23 (1)
19-23:⚠️ Potential issue | 🟡 MinorDon't localize timestamps inside the data-access layer.
toLocaleDateString()/toLocaleTimeString()here run with the server's locale and timezone, so every user gets server-formatted dates. Return raw ISO strings orDatevalues instead and format them closer to the UI where the viewer locale is known.Also applies to: 47-50, 58-58
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/data/chat.ts` around lines 19 - 23, The data-access layer is converting dates to localized strings; update the mapping(s) that set timestamp (currently using new Date(c.updatedAt).toLocaleDateString()) and any other toLocale* usages (reported at the blocks around lines 47-50 and 58) to return raw ISO or Date values instead — e.g., replace toLocaleDateString()/toLocaleTimeString() calls with new Date(...).toISOString() or with the Date object itself (keep property names like timestamp, createdAt, updatedAt in the same DTOs such as the chat mapping that produces timestamp and any similar mappings), so formatting is deferred to the UI.components/auth-form.tsx-160-165 (1)
160-165:⚠️ Potential issue | 🟡 MinorWire the reset-password link or remove it for now.
href="#"turns “Forgot your password?” into a dead control.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/auth-form.tsx` around lines 160 - 165, The "Forgot your password?" anchor in components/auth-form.tsx is using href="#" which creates a dead control; either wire it to the real reset flow or remove it. Replace the placeholder anchor with a real link to your password-reset route (e.g., use Next.js <Link> or set href="/reset-password") or remove the anchor and conditionally render nothing until reset-password is implemented; ensure any click handler (e.g., openResetModal or navigateToReset) prevents default only when appropriate and that the anchor text remains accessible (aria-label/role) if kept.components/chat/chat-event.tsx-306-334 (1)
306-334:⚠️ Potential issue | 🟡 MinorUse absolute thresholds when choosing the relative-time unit.
diffInSeconds < 60is true for every future timestamp because the value is negative, so something two days ahead becomes"in 172800 seconds"instead of"in 2 days".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/chat/chat-event.tsx` around lines 306 - 334, The getRelativeTimeString function uses diffInSeconds = now - date so future dates produce negative thresholds; change the calculation to use the signed delta (e.g., deltaSeconds = Math.floor((date.getTime() - now.getTime()) / 1000)) and use its absolute value for unit-selection thresholds (absSeconds) while passing the signed deltaSeconds into rtf.format calls (and similarly for minutes/hours/days) so unit choice uses absolute magnitude but formatting preserves past/future sign; update references to diffInSeconds in the comparisons and rtf.format invocations accordingly.components/chat-area.tsx-52-61 (1)
52-61:⚠️ Potential issue | 🟡 MinorReset optimistic document chips when the active chat changes.
Lines 58-61 reset
localMessages, butdocumentsstays untouched. After switching chats, the previous chat's uploaded files can still render under the new chat header.Suggested fix
useEffect(() => { setLocalMessages(messages); }, [messages, currentChat?.id]); + + useEffect(() => { + setDocuments([]); + }, [currentChat?.id]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/chat-area.tsx` around lines 52 - 61, When the active chat changes the useEffect that currently calls setLocalMessages(messages) should also reset optimistic uploaded documents: inside the same effect (the one referencing messages and currentChat?.id) call setDocuments([]) and also clear the file input value if present (fileInputRef.current.value = ''), so the UploadedDocument state and input are cleared when switching chats; update the useEffect that references setLocalMessages, messages and currentChat?.id to include setDocuments and the fileInputRef clearing logic.
🧹 Nitpick comments (6)
checkpoint-8142/config.json (1)
1-61: Consider whether checkpoint files should be included in this PR.The changes to this file are purely cosmetic formatting adjustments (normalizing
1e-06to1e-6and condensing thearchitecturesarray). While the formatting is valid and harmless, these machine learning checkpoint files appear unrelated to the stated PR objectives of refactoring from FastAPI to Next.js with server-side rendering.Checkpoint and model training state files are typically generated artifacts rather than source code. Consider whether these files should be:
- Excluded from version control entirely (via
.gitignore)- Committed in a separate PR focused on model updates
- Kept out of application architecture refactoring PRs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@checkpoint-8142/config.json` around lines 1 - 61, This PR unintentionally includes a ML checkpoint config (config.json) with only cosmetic formatting changes; revert the file changes from this refactor PR and either remove checkpoint artifacts from version control by adding an appropriate ignore rule (e.g., ignore the checkpoint directory or model config/artifact patterns) to .gitignore, or move the legitimate model changes into a separate, dedicated PR; locate the file named config.json (keys like "architectures", "layer_norm_epsilon", "vocab_size") to revert and ensure you add the checkpoint path/pattern to .gitignore before committing the refactor-only changes.checkpoint-8142/trainer_state.json (1)
1-625: Cosmetic formatting changes unrelated to PR objectives.All modifications in this file normalize scientific notation in
learning_ratevalues (e.g.,e-05→e-5). These changes are purely cosmetic with no functional impact on the training state.As with
config.json, this generated training state file appears unrelated to the Next.js/FastAPI refactoring described in the PR objectives. The large number of formatting changes (60+ lines) creates diff noise without adding value to the application architecture refactor.Recommendation: Exclude checkpoint and training state artifacts from this PR to keep the review focused on the actual application changes.
backend/core/database.py (1)
13-13: Unused import after refactoring to lazy initialization.The
get_generation_serviceimport is no longer used in this file since eager initialization was removed. Consider removing it to keep the module clean.🧹 Remove unused import
-from backend.services.generation_service import get_generation_service🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/core/database.py` at line 13, Remove the now-unused import get_generation_service from the top-level imports in this module (it was left over after switching to lazy initialization); locate the import statement referencing get_generation_service and delete it, then run lint/tests to ensure no other references remain (e.g., search for get_generation_service in database.py to confirm).backend/services/generation_service.py (1)
173-173: Consider using script-relative path for robustness across different deployment contexts.The relative path
./checkpoint-8142is resolved relative to the current working directory. While this works when the FastAPI server starts from the repository root (the standard deployment pattern), it becomes fragile in other contexts—such as when the service is started from a different directory, containerized, or deployed via certain application servers.🔧 Suggested improvement: Use script-relative path
- checkpoint_path = "./checkpoint-8142" + checkpoint_path = os.path.join(os.path.dirname(__file__), "..", "..", "checkpoint-8142")Alternatively, make the checkpoint path configurable via environment variable for better deployment flexibility.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/services/generation_service.py` at line 173, The hardcoded relative checkpoint_path ("./checkpoint-8142") is fragile; change it to resolve relative to the service module and/or be configurable via environment variable: read a CHECKPOINT_PATH env var first, else compute a script-relative path using the module's location (e.g., derive from __file__ of generation_service.py) and assign that to checkpoint_path so the code using checkpoint_path continues to work reliably across deployment contexts.lib/auth.ts (1)
9-13: Hardcoded IP intrustedOriginsmay cause issues across environments.The IP
192.168.20.8:3000is environment-specific. Consider using an environment variable for additional trusted origins to avoid hardcoding local network addresses.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/auth.ts` around lines 9 - 13, The trustedOrigins array currently hardcodes a local IP ("192.168.20.8:3000"); update the code that defines trustedOrigins to read an environment variable (e.g., ADDITIONAL_TRUSTED_ORIGINS) and append any comma-separated origins from it to the array instead of hardcoding the IP. Locate the trustedOrigins definition in lib/auth.ts and modify the initialization so it preserves the existing defaults ("http://localhost:3000", "http://127.0.0.1:3000") and conditionally merges parsed values from process.env.ADDITIONAL_TRUSTED_ORIGINS (trim each entry and ignore empty strings) into trustedOrigins.app/chat/page.tsx (1)
7-16: Consider the duplicategetUserChatscall between layout and page.Both
app/chat/layout.tsx(line 26) and this page callgetUserChats(session.user.id). Since Prisma queries aren't automatically deduplicated likefetchrequests, this results in two database queries for the same data on the/chatroute.The layout needs the chats for the sidebar, but this page only uses them to redirect. Consider passing the chats from layout via React context or restructuring to avoid the redundant query.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/chat/page.tsx` around lines 7 - 16, The page is duplicating the database query performed in the chat layout: remove the getUserChats call from ChatIndexPage and either (A) have the layout perform the redirect when chats exist (move the redirect logic into app/chat/layout.tsx after its getUserChats call), or (B) fetch chats once in app/chat/layout.tsx and expose them to child pages via a React context/provider so ChatIndexPage can read the chats without re-querying; update ChatIndexPage to use the provided context (or rely on the layout redirect) and remove its call to getUserChats and related redirect logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/auth-form.tsx`:
- Around line 216-219: The placeholder consent links inside the FieldDescription
component must be replaced with real, configurable URLs before release: update
the anchor hrefs currently set to "#" (in the auth form instances) to point to
the actual Terms of Service and Privacy Policy endpoints (preferably pulled from
a central config/constants or environment variables such as TERMS_URL and
PRIVACY_URL or from your site settings), and ensure both occurrences (the one
around FieldDescription and the other auth-screen occurrence) use the same
shared constants or props so they stay synced and testable.
In `@components/chat-area.tsx`:
- Around line 242-253: The placeholder "Ready to assist" is gated on
messages.length but optimistic sends update localMessages first, so change the
conditional that renders the placeholder in the JSX block to use localMessages
(or localMessages.length || messages.length) instead of messages; locate the
ternary rendering around the placeholder (the block referencing messages.length)
and update it to check localMessages to ensure the optimistic first message
shows user/typing events immediately.
In `@components/chat-sidebar.tsx`:
- Around line 59-61: Guard non-idempotent submit handlers by adding an in-flight
boolean (e.g., isCreatingChat) and early-return if true inside handleCreateChat;
set isCreatingChat=true before calling createChatAction() and only reset it to
false in both success and catch paths. Do not clear or close the new-chat dialog
state (isNewChatDialogOpen, newChatTitle) until after createChatAction()
resolves successfully; on success close/clear, on error keep the dialog open and
show/propagate the error. Apply the same pattern to the other submit handlers
that call non-idempotent actions (the handlers around the other submit blocks
referenced in the comment) to prevent duplicate submissions and ensure state is
only cleared after success.
In `@components/chat/chat-event.tsx`:
- Around line 381-401: The component currently computes formattedTime (and
resolvedLocale) during render which is not SSR-stable; change formatting so
server render does not depend on navigator or current time: keep isoString as
before but move the locale- and time-dependent formatting out of useMemo and
into a client-only update (useEffect + useState) that runs only when mounted;
specifically, stop using navigator.language inside resolvedLocale on render, and
compute resolvedLocale and formattedTime inside an effect (or only compute
formattedTime on the client) and render a stable server fallback (e.g.,
isoString or a neutral placeholder) when locale is undefined or format ===
"relative", while still using getRelativeTimeString, FORMAT_PRESETS, and
Intl.DateTimeFormat on the client for the final visible output.
In `@components/chat/chat-toolbar.tsx`:
- Around line 122-127: The handleKeyDown currently calls onSubmit before
onKeyDown and doesn't check IME composition state; update handleKeyDown to
invoke onKeyDown(e) first, then only call onSubmit when Enter is pressed, the
NEWLINE_MODIFIER_KEY is not held, and composition is not in progress (check
e.nativeEvent.isComposing or a tracked composition flag). Reference the
handleKeyDown function and the onKeyDown/onSubmit handlers; optionally add
onCompositionStart/onCompositionEnd to track composition state for maximum
robustness.
In `@lib/actions/chat.ts`:
- Around line 118-122: The timestamp returned for newly generated messages
(e.g., the failMessage object created in lib/actions/chat.ts) is currently a
localized time string; change it to return a full machine-readable timestamp
(ISO 8601 or numeric ms since epoch) matching the shape parsed in
components/chat-area.tsx so ChatEventTime and date dividers work
correctly—replace calls like new
Date(failMessage.createdAt).toLocaleTimeString(...) with new
Date(failMessage.createdAt).toISOString() (or Date.now()/createdAt as a number)
for both the failure path and the optimistic message path (the similar block
around lines 151-155) so the component receives the same timestamp shape it
expects.
- Around line 49-56: The delete code in lib/actions/chat.ts uses
process.env.BACKEND_URL and falls back to localhost; change it to use the same
base as the other actions by reading process.env.BACKEND_API_BASE (with the same
localhost fallback) when building backendUrl used in the fetch to
`/api/v1/model/${chatId}`; update the variable referenced in the try block
(backendUrl) so sendMessageAction and uploadDocumentAction and this delete call
all share BACKEND_API_BASE behavior.
- Around line 95-127: The fetch to `${BACKEND_API_BASE}/model/generate` and the
subsequent response.json() must be wrapped in a try-catch so network errors,
timeouts, or invalid JSON are handled; inside the catch create the same
assistant fallback message via prisma.message.create (the same shape used when
!response.ok), call revalidatePath(`/chat`), and return the expected { success:
true, message: { id, role, content, timestamp } } structure; update the block
around the existing response/const result/aiContent handling to use the
try-catch and avoid leaving an orphaned user message if fetch or JSON parsing
fails.
In `@lib/data/chat.ts`:
- Around line 30-40: The query currently calls prisma.chat.findUnique({ where: {
id: chatId }, ... }) and then checks chat.userId !== userId after fetching;
update the DB query to enforce ownership so unauthorized chats are never loaded.
Replace prisma.chat.findUnique with prisma.chat.findFirst (or findUnique using a
composite key if available) and add userId to the where clause (e.g., where: {
id: chatId, userId }) so the DB only returns chats belonging to the user, then
remove the separate chat.userId !== userId check.
In `@package.json`:
- Line 69: Move "@prisma/client" from devDependencies to dependencies in
package.json and update its major version to match the installed "prisma"
package (align major versions with prisma@7.x); ensure the runtime import in
lib/prisma.ts (import { PrismaClient }) is satisfied in production by installing
the dependency and bumping the `@prisma/client` version to the same major as
prisma to avoid compatibility issues.
In `@prisma/schema.prisma`:
- Around line 41-58: Add a composite unique constraint on the Account model to
prevent duplicate external identities by enforcing uniqueness of (providerId,
accountId); update the Prisma model named Account to include a
@@unique([providerId, accountId]) (or equivalent) so lookups and relations that
use providerId/accountId are deterministic and duplicates cannot be created.
- Around line 76-77: The migration makes Chat.userId (Chat.userId) a required FK
referencing User.id without backfilling rows that default to "guest"; update the
migration to ensure referential integrity before adding the constraint by either
creating a corresponding User record with id = "guest" (and sensible metadata)
or updating all existing Chat rows to point to an existing real user id, then
add the FK constraint; also remove or adjust the default "guest" in the
application model (backend/core/models.py) to avoid reintroducing invalid
values.
---
Outside diff comments:
In `@backend/api/v1/ingestion_routes.py`:
- Around line 68-84: The ingest_document endpoint currently leaves chat document
count mutation to external callers which risks state drift; update the
ingest_document implementation (the ingest_document function and related
IngestionService logic) so the SQL chat document count is updated
transactionally as part of the same backend workflow (or emit a durable backend
event from IngestionService on success) and ensure the docstring reflects this
behavior; locate where IngestionService.ingest (or similar) stores embeddings
and add a DB transaction or reliable event emission to increment the chat's
documentCount within that same operation, and update the ingest_document
docstring wording that claims the SQL update happens here.
In `@components/ui/label.tsx`:
- Around line 1-24: There is a duplicate, unused Label component implementation
that matches the exported Label in components/ui/label.tsx; remove the dead file
components/label.tsx to avoid maintenance overhead and potential confusion, and
verify that all imports (e.g., any references to Label) use components/ui/label
so no import paths break after deletion; if any stray import points to
components/label.tsx, update them to import the Label from components/ui/label
before deleting the duplicate.
In `@components/ui/sidebar.tsx`:
- Around line 154-166: The Sidebar component currently types its public API as
React.ComponentProps<"div"> but forwards those props inconsistently: props like
className and other DOM attributes are spread onto the mobile <Sheet> instead of
the actual rendered panel, and dir is only forwarded on mobile; fix this by
ensuring all remaining div props (including className, dir and any other DOM
props from the rest/spread) are forwarded to the rendered sidebar panel element
in both mobile and desktop branches. Concretely, keep the Sidebar signature and
rest spread as-is, remove spreading rest directly onto <Sheet>, and instead pass
only Sheet-specific props to <Sheet> while forwarding the original rest props
(or a filtered subset) to the panel element (the panel component rendered inside
<Sheet> and the desktop panel element) so className and dir are applied
consistently across breakpoints; use explicit prop names (e.g., className, dir)
or a small helper to split sheetProps vs panelProps if needed.
In `@lib/validation/auth.ts`:
- Around line 74-79: The parseSignupFormData helper is missing the
confirmPassword field required by signupSchema; update parseSignupFormData (and
any callers relying on it) to read confirmPassword from the FormData and include
it in the object passed to signupSchema.safeParse (e.g., add confirmPassword:
String(formData.get("confirmPassword") || "")), or alternatively change the
helper to use serverSignupSchema if that was intended; ensure the returned
safeParse input matches whichever schema (signupSchema or serverSignupSchema) is
actually expected.
In `@README.md`:
- Around line 45-49: The README currently mixes a Supabase-specific DATABASE_URL
example with a generic PostgreSQL instruction; update the .env example and text
so the DATABASE_URL shown is a generic Postgres connection string
(DATABASE_URL="postgresql://<USER>:<PASSWORD>@<HOST>:5432/<DATABASE_NAME>") and
add a brief note that Supabase users should replace <HOST> with the Supabase DB
host (db.<YOUR_SUPABASE_REF>.supabase.co) and <DATABASE_NAME> with "postgres";
ensure this change applies to both the root and backend .env instructions and
any surrounding text that references Supabase vs PostgreSQL.
---
Minor comments:
In `@app/chat/loading.tsx`:
- Line 6: The class "w-75" in the div in app/chat/loading.tsx is not a valid
Tailwind width utility; replace it with a valid Tailwind width class (for
example w-72 or w-80) or an arbitrary value like w-[75%] depending on the
intended width so the sidebar actually gets a width on md+ screens; update the
class string on the div node that currently contains "w-75 shrink-0 border-r
border-sidebar-border hidden md:flex flex-col" accordingly.
In `@app/layout.tsx`:
- Around line 8-9: The _geist and _geistMono font variables are imported/created
but never applied; update the root layout so the body element includes the
generated classNames from _geist and/or _geistMono (e.g., combine
_geist.className and _geistMono.className with existing "font-sans") to actually
apply the fonts, or if you don't intend to use them remove the Geist(...) and
Geist_Mono(...) calls to avoid loading unused fonts; specifically modify the
element that renders the body (where "font-sans" is used) to include the font
variables (_geist.className and/or _geistMono.className) or delete the unused
_geist/_geistMono declarations.
In `@backend/api/v1/model_routes.py`:
- Around line 32-34: The except block in model_routes.py currently logs the
exception and re-raises an HTTPException without chaining, losing the original
traceback; update the handler so the raised HTTPException is chained from the
caught exception (use "raise HTTPException(status_code=500, detail='Failed to
generate answer') from e") and keep the existing logger.error(f"Generate error:
{e}") call so the original exception context is preserved; locate the except
Exception as e block surrounding the generate logic and modify the raise to use
"from e".
- Around line 23-29: The current truthiness check on context_str can be true if
it contains only whitespace (e.g., "\n\n"), causing
generation_service.generate_answer to be called with an empty context; change
the logic that builds and checks context to trim and validate non-empty document
texts (e.g., filter search_results.results for doc.text.strip() truthy before
joining) and only call get_generation_service() /
generation_service.generate_answer(request.query, context_str) when the
resulting context_str after strip/join is non-empty; otherwise set ai_content to
the fallback message. Ensure you update references to context_str,
search_results.results, get_generation_service, and
generation_service.generate_answer in the modified block.
- Around line 39-45: The route currently ignores the DeleteCollectionResponse
from vector_service.delete_collection(chat_id) and always returns success;
update the code to capture the response (e.g., resp =
vector_service.delete_collection(chat_id)), check resp.success, and return
{"status":"success","message": resp.message} when true or
{"status":"warning","message": resp.message} when false, while logging the
failure via logger.warning with the response.message; use get_vector_service()
and vector_service.delete_collection(chat_id) as the locating symbols and ensure
exceptions are still caught as before.
In `@backend/main.py`:
- Around line 83-85: The /info endpoint payload was not updated after switching
to model_router: update the function that builds the /info response (the handler
that returns the info metadata) to replace the old vector/chat-oriented keys
with the new model_router-related keys and values (e.g., rename or remove former
vector/chat entries and add the model_router endpoints and capabilities),
ensuring the returned JSON reflects the current routes registered under
model_router and any changed field names.
In `@components/auth-form.tsx`:
- Around line 160-165: The "Forgot your password?" anchor in
components/auth-form.tsx is using href="#" which creates a dead control; either
wire it to the real reset flow or remove it. Replace the placeholder anchor with
a real link to your password-reset route (e.g., use Next.js <Link> or set
href="/reset-password") or remove the anchor and conditionally render nothing
until reset-password is implemented; ensure any click handler (e.g.,
openResetModal or navigateToReset) prevents default only when appropriate and
that the anchor text remains accessible (aria-label/role) if kept.
In `@components/chat-area.tsx`:
- Around line 52-61: When the active chat changes the useEffect that currently
calls setLocalMessages(messages) should also reset optimistic uploaded
documents: inside the same effect (the one referencing messages and
currentChat?.id) call setDocuments([]) and also clear the file input value if
present (fileInputRef.current.value = ''), so the UploadedDocument state and
input are cleared when switching chats; update the useEffect that references
setLocalMessages, messages and currentChat?.id to include setDocuments and the
fileInputRef clearing logic.
In `@components/chat/chat-event.tsx`:
- Around line 306-334: The getRelativeTimeString function uses diffInSeconds =
now - date so future dates produce negative thresholds; change the calculation
to use the signed delta (e.g., deltaSeconds = Math.floor((date.getTime() -
now.getTime()) / 1000)) and use its absolute value for unit-selection thresholds
(absSeconds) while passing the signed deltaSeconds into rtf.format calls (and
similarly for minutes/hours/days) so unit choice uses absolute magnitude but
formatting preserves past/future sign; update references to diffInSeconds in the
comparisons and rtf.format invocations accordingly.
In `@components/skeleton.tsx`:
- Around line 1-13: Remove the duplicate Skeleton component by deleting the
redundant function export (Skeleton) in this file; ensure the project uses the
single canonical Skeleton implementation (the other Skeleton symbol already used
across the codebase) and remove any exports or barrel-export entries that
reference this duplicate so nothing imports it; after removing the file, run a
repo-wide search and a build/test to confirm no remaining references to this
Skeleton remain and update any import paths if necessary.
In `@components/ui/field.tsx`:
- Around line 194-197: The current truthy check in the useMemo that builds
`content` inside the FieldError/Field component skips valid renderable values
like 0 or ""; change the guard from a boolean/truthy test to an explicit nullish
check (e.g., use `children != null` or `children !== null && children !==
undefined`) in the `useMemo` block where `content` is computed so `0` and empty
string render correctly; update the `content` creation in the `useMemo` that
references `children` (and any similar checks in the same component) to use this
nullish check.
In `@components/ui/sidebar.tsx`:
- Around line 610-612: The SidebarMenuSkeleton component computes width using
Math.random() which will cause SSR/client hydration mismatches; replace the
random calculation in the width useMemo with a stable deterministic value (e.g.,
a fixed percentage string like "60%") or derive it from a deterministic prop,
removing Math.random() entirely; update the const width = React.useMemo(...) in
SidebarMenuSkeleton so it returns a constant string and ensure any tests or
usages reference that stable width.
In `@docs/RAG_PIPELINE.md`:
- Line 83: The inline code span in the "Separators" line contains a trailing
space inside the backticks (`. `) which triggers markdownlint MD038; edit the
text so the code span has no internal trailing space by replacing `. ` with `.`
(i.e., use `.` instead of `. `) in that inline code snippet.
In `@lib/auth.ts`:
- Around line 14-19: The current cast of
process.env.GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET to string in the
socialProviders.google config is unsafe; instead validate and fail fast: replace
the direct casts with a required-env check (e.g., call a small helper like
getRequiredEnv('GOOGLE_CLIENT_ID') and getRequiredEnv('GOOGLE_CLIENT_SECRET') or
inline if-checks) and throw a clear Error when missing, then use the validated
values for socialProviders.google.clientId and .clientSecret so you never pass
literal "undefined" to the OAuth provider.
In `@lib/data/chat.ts`:
- Around line 19-23: The data-access layer is converting dates to localized
strings; update the mapping(s) that set timestamp (currently using new
Date(c.updatedAt).toLocaleDateString()) and any other toLocale* usages (reported
at the blocks around lines 47-50 and 58) to return raw ISO or Date values
instead — e.g., replace toLocaleDateString()/toLocaleTimeString() calls with new
Date(...).toISOString() or with the Date object itself (keep property names like
timestamp, createdAt, updatedAt in the same DTOs such as the chat mapping that
produces timestamp and any similar mappings), so formatting is deferred to the
UI.
---
Nitpick comments:
In `@app/chat/page.tsx`:
- Around line 7-16: The page is duplicating the database query performed in the
chat layout: remove the getUserChats call from ChatIndexPage and either (A) have
the layout perform the redirect when chats exist (move the redirect logic into
app/chat/layout.tsx after its getUserChats call), or (B) fetch chats once in
app/chat/layout.tsx and expose them to child pages via a React context/provider
so ChatIndexPage can read the chats without re-querying; update ChatIndexPage to
use the provided context (or rely on the layout redirect) and remove its call to
getUserChats and related redirect logic.
In `@backend/core/database.py`:
- Line 13: Remove the now-unused import get_generation_service from the
top-level imports in this module (it was left over after switching to lazy
initialization); locate the import statement referencing get_generation_service
and delete it, then run lint/tests to ensure no other references remain (e.g.,
search for get_generation_service in database.py to confirm).
In `@backend/services/generation_service.py`:
- Line 173: The hardcoded relative checkpoint_path ("./checkpoint-8142") is
fragile; change it to resolve relative to the service module and/or be
configurable via environment variable: read a CHECKPOINT_PATH env var first,
else compute a script-relative path using the module's location (e.g., derive
from __file__ of generation_service.py) and assign that to checkpoint_path so
the code using checkpoint_path continues to work reliably across deployment
contexts.
In `@checkpoint-8142/config.json`:
- Around line 1-61: This PR unintentionally includes a ML checkpoint config
(config.json) with only cosmetic formatting changes; revert the file changes
from this refactor PR and either remove checkpoint artifacts from version
control by adding an appropriate ignore rule (e.g., ignore the checkpoint
directory or model config/artifact patterns) to .gitignore, or move the
legitimate model changes into a separate, dedicated PR; locate the file named
config.json (keys like "architectures", "layer_norm_epsilon", "vocab_size") to
revert and ensure you add the checkpoint path/pattern to .gitignore before
committing the refactor-only changes.
In `@lib/auth.ts`:
- Around line 9-13: The trustedOrigins array currently hardcodes a local IP
("192.168.20.8:3000"); update the code that defines trustedOrigins to read an
environment variable (e.g., ADDITIONAL_TRUSTED_ORIGINS) and append any
comma-separated origins from it to the array instead of hardcoding the IP.
Locate the trustedOrigins definition in lib/auth.ts and modify the
initialization so it preserves the existing defaults ("http://localhost:3000",
"http://127.0.0.1:3000") and conditionally merges parsed values from
process.env.ADDITIONAL_TRUSTED_ORIGINS (trim each entry and ignore empty
strings) into trustedOrigins.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7d4fa66b-720b-4d94-84c2-0540b9e68f65
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (74)
README.mdapp/api/auth/[...all]/route.tsapp/chat/[chatId]/page.tsxapp/chat/layout.tsxapp/chat/loading.tsxapp/chat/page.tsxapp/dashboard/page.tsxapp/globals.cssapp/layout.tsxapp/login/loading.tsxapp/login/page.tsxapp/page.tsxapp/signup/loading.tsxapp/signup/page.tsxbackend/api/v1/chat_routes.pybackend/api/v1/ingestion_routes.pybackend/api/v1/model_routes.pybackend/core/database.pybackend/main.pybackend/services/generation_service.pycheckpoint-8142/config.jsoncheckpoint-8142/trainer_state.jsoncomponents/auth-form.tsxcomponents/chat-area.tsxcomponents/chat-interface.tsxcomponents/chat-sidebar.tsxcomponents/chat/chat-event.tsxcomponents/chat/chat-header.tsxcomponents/chat/chat-messages.tsxcomponents/chat/chat-toolbar.tsxcomponents/chat/chat.tsxcomponents/dropdown-menu.tsxcomponents/label.tsxcomponents/mode-toggle.tsxcomponents/scroll-area.tsxcomponents/separator.tsxcomponents/sheet.tsxcomponents/skeleton.tsxcomponents/textarea.tsxcomponents/theme-provider.tsxcomponents/tooltip.tsxcomponents/ui/avatar.tsxcomponents/ui/button.tsxcomponents/ui/card.tsxcomponents/ui/dialog.tsxcomponents/ui/dropdown-menu.tsxcomponents/ui/field.tsxcomponents/ui/input.tsxcomponents/ui/label.tsxcomponents/ui/resizable.tsxcomponents/ui/scroll-area.tsxcomponents/ui/separator.tsxcomponents/ui/sheet.tsxcomponents/ui/sidebar.tsxcomponents/ui/skeleton.tsxcomponents/ui/sonner.tsxcomponents/ui/textarea.tsxcomponents/ui/tooltip.tsxdocs/API_REFERENCE.mddocs/RAG_PIPELINE.mdhooks/use-mobile.tslib/actions/chat.tslib/auth-client.tslib/auth.tslib/data/chat.tslib/prisma.tslib/types.tslib/utils.tslib/validation/auth.tspackage.jsonprisma.config.tsprisma/schema.prismaproxy.tsserver/users.ts
💤 Files with no reviewable changes (4)
- prisma.config.ts
- server/users.ts
- components/chat-interface.tsx
- backend/api/v1/chat_routes.py
| <FieldDescription className="px-6 text-center"> | ||
| By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "} | ||
| and <a href="#">Privacy Policy</a>. | ||
| </FieldDescription> |
There was a problem hiding this comment.
Replace the placeholder Terms/Privacy links before release.
Both auth screens ask for consent, but the links still point to #. On a signup/login surface, that makes the consent flow non-functional and creates compliance risk.
Also applies to: 429-432
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/auth-form.tsx` around lines 216 - 219, The placeholder consent
links inside the FieldDescription component must be replaced with real,
configurable URLs before release: update the anchor hrefs currently set to "#"
(in the auth form instances) to point to the actual Terms of Service and Privacy
Policy endpoints (preferably pulled from a central config/constants or
environment variables such as TERMS_URL and PRIVACY_URL or from your site
settings), and ensure both occurrences (the one around FieldDescription and the
other auth-screen occurrence) use the same shared constants or props so they
stay synced and testable.
| const resolvedLocale = | ||
| locale ?? (typeof navigator !== "undefined" ? navigator.language : "en-US"); | ||
|
|
||
| const formattedTime = useMemo(() => { | ||
| if (format === "relative") { | ||
| return getRelativeTimeString(date, resolvedLocale); | ||
| } | ||
|
|
||
| const options = formatOptions ?? FORMAT_PRESETS[format]; | ||
| return new Intl.DateTimeFormat(resolvedLocale, options).format(date); | ||
| }, [date, format, formatOptions, resolvedLocale]); | ||
|
|
||
| const isoString = useMemo(() => date.toISOString(), [date]); | ||
|
|
||
| return ( | ||
| <time | ||
| dateTime={isoString} | ||
| className={cn("text-xs text-muted-foreground", className)} | ||
| {...props} | ||
| > | ||
| {formattedTime} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Next.js App Router, are Client Components prerendered on the server, and can rendering navigator.language, Intl.DateTimeFormat defaults, or current-time-dependent strings during render cause hydration mismatches?
💡 Result:
Yes.
- Client Components are still prerendered (SSR/SSG) into HTML on the server as part of the App Router render pipeline: Next.js renders Server Components to the RSC payload, and uses Client Components + the RSC payload to prerender HTML for the initial response; then the browser hydrates Client Components to make them interactive. (nextjs.org)
- Because of that, anything that can produce different output between the server prerender and the browser’s first render can cause a hydration mismatch, including:
- browser-only values (e.g.
navigator.language) (nextjs.org) - time-dependent output (e.g.
Date(), “current time”,Date.now()-style strings) (nextjs.org) - default-locale / default-timezone formatting differences, which is exactly what you get if you rely on
Intl.DateTimeFormat()defaults (they depend on the runtime’s locale/time zone—Node/server vs browser/client can differ). (nextjs.org)
- browser-only values (e.g.
Typical fixes are: render a stable placeholder on the server and fill in client-only/time/locale data in useEffect, disable SSR for that component via dynamic(..., { ssr: false }), or use suppressHydrationWarning for unavoidable one-off text (like timestamps). (nextjs.org)
Citations:
- 1: https://nextjs.org/docs/app/getting-started/server-and-client-components
- 2: https://nextjs.org/docs/messages/react-hydration-error
- 3: https://nextjs.org/docs/messages/react-hydration-error
- 4: https://nextjs.org/docs/messages/react-hydration-error
- 5: https://nextjs.org/docs/messages/react-hydration-error
🏁 Script executed:
#!/bin/bash
# Check if the file exists and verify the code snippet
if [ -f "components/chat/chat-event.tsx" ]; then
echo "=== File found, reading lines 381-401 ==="
sed -n '381,401p' components/chat/chat-event.tsx
else
echo "File not found at expected path, searching for it..."
find . -name "chat-event.tsx" -type f
fiRepository: PointerRAG/Project
Length of output: 759
🏁 Script executed:
#!/bin/bash
# Search for getRelativeTimeString function to understand time-dependency
rg "getRelativeTimeString" -B 2 -A 10Repository: PointerRAG/Project
Length of output: 1669
🏁 Script executed:
#!/bin/bash
# Check how ChatEventTime component is used
rg "ChatEventTime|chat-event" -t tsx -t ts --max-count=20 | head -50Repository: PointerRAG/Project
Length of output: 89
This render-time formatting is not SSR-stable.
When locale is omitted, the server render uses "en-US" but hydration reruns with navigator.language, causing locale-based formatting differences. Additionally, the "relative" format calls getRelativeTimeString, which uses new Date() to calculate elapsed time. Since the server and client render at different times, the relative timestamp will differ—e.g., "1 second ago" becomes "2 seconds ago" by the time hydration occurs. This causes hydration mismatches.
Pass a stable locale from the caller, or defer the formatted output to a useEffect to avoid SSR altogether.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/chat/chat-event.tsx` around lines 381 - 401, The component
currently computes formattedTime (and resolvedLocale) during render which is not
SSR-stable; change formatting so server render does not depend on navigator or
current time: keep isoString as before but move the locale- and time-dependent
formatting out of useMemo and into a client-only update (useEffect + useState)
that runs only when mounted; specifically, stop using navigator.language inside
resolvedLocale on render, and compute resolvedLocale and formattedTime inside an
effect (or only compute formattedTime on the client) and render a stable server
fallback (e.g., isoString or a neutral placeholder) when locale is undefined or
format === "relative", while still using getRelativeTimeString, FORMAT_PRESETS,
and Intl.DateTimeFormat on the client for the final visible output.
| const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { | ||
| if (e.key === "Enter" && !e[NEWLINE_MODIFIER_KEY]) { | ||
| e.preventDefault(); | ||
| onSubmit?.(); | ||
| } | ||
| onKeyDown?.(e); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n components/chat/chat-toolbar.tsx | sed -n '110,140p'Repository: PointerRAG/Project
Length of output: 1091
🏁 Script executed:
rg -i "isComposing|composition" --type typescript --type tsxRepository: PointerRAG/Project
Length of output: 96
🏁 Script executed:
head -50 components/chat/chat-toolbar.tsxRepository: PointerRAG/Project
Length of output: 1575
🏁 Script executed:
rg "isComposing|composition" --type tsxRepository: PointerRAG/Project
Length of output: 89
🏁 Script executed:
rg "NEWLINE_MODIFIER_KEY" components/chat/chat-toolbar.tsx -B 5 -A 2Repository: PointerRAG/Project
Length of output: 526
🏁 Script executed:
wc -l components/chat/chat-toolbar.tsxRepository: PointerRAG/Project
Length of output: 98
🏁 Script executed:
cat -n components/chat/chat-toolbar.tsx | sed -n '1,60p'Repository: PointerRAG/Project
Length of output: 2265
🏁 Script executed:
web_search
React.KeyboardEvent nativeEvent isComposing property MDNRepository: PointerRAG/Project
Length of output: 170
🌐 Web query:
React KeyboardEvent nativeEvent isComposing IME composition
💡 Result:
In React, onKeyDown/onKeyUp receive a SyntheticKeyboardEvent whose nativeEvent is the browser’s real KeyboardEvent. During IME (Japanese/Chinese/Korean) composition you can check:
e.nativeEvent.isComposing→trueaftercompositionstartand beforecompositionend(i.e., “currently composing”). [1]
Common pattern (avoid submitting on Enter while composing):
function onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.nativeEvent.isComposing) return; // IME in progress
if (e.key === "Enter") {
// submit
}
}Important edge case: the “confirm/commit” Enter at the end of IME often happens when composition has just ended, so isComposing can be false for that Enter—meaning isComposing alone may not block every “IME confirm” Enter. Many apps additionally track composition explicitly via React’s composition events. [2][3]
const composingRef = useRef(false);
return (
<input
onCompositionStart={() => (composingRef.current = true)}
onCompositionEnd={() => (composingRef.current = false)}
onKeyDown={(e) => {
if (composingRef.current) return;
if (e.key === "Enter") {/* submit */}
}}
/>
);TypeScript note: if you get typing errors accessing isComposing, ensure your TS lib includes DOM, or cast e.nativeEvent to KeyboardEvent (the property is part of the standard KeyboardEvent API). [1]
Sources: MDN on KeyboardEvent.isComposing [1], React composition events (React issue / docs references) [3], and a write-up noting Enter-at-confirm can have isComposing === false [2].
[1] (developer.mozilla.org)
[2] (qiita.com)
[3] (github.com)
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/isComposing?utm_source=openai
- 2: https://qiita.com/ledsun/items/31e43a97413dd3c8e38e?utm_source=openai
- 3: Composition Events(Chinese, Japanese IME) problem in controlled components(input, textarea) facebook/react#8683
🏁 Script executed:
rg "keydown|keypress|keyup" --type txt -lRepository: PointerRAG/Project
Length of output: 44
🏁 Script executed:
fd -e tsx -e ts | head -20Repository: PointerRAG/Project
Length of output: 546
🏁 Script executed:
rg -i "newline" components/chat/chat-toolbar.tsxRepository: PointerRAG/Project
Length of output: 168
Don't submit while IME text composition is in progress.
Line 123 submits on bare Enter, which interrupts IME composition for CJK users. When composing with JP/CN/KR IMEs, Enter is used to confirm composition, so the current code sends the message mid-composition. The issue is compounded by calling onKeyDown after submission, preventing consumers from cancelling the send.
Move onKeyDown to execute first and check the composition state before submission:
Suggested fix
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
+ onKeyDown?.(e);
+ if (e.defaultPrevented || e.nativeEvent.isComposing) return;
+
if (e.key === "Enter" && !e[NEWLINE_MODIFIER_KEY]) {
e.preventDefault();
onSubmit?.();
}
- onKeyDown?.(e);
};Note: isComposing covers most cases, though the final "confirm Enter" may fire with isComposing === false. For maximum robustness, consider tracking composition state explicitly with onCompositionStart and onCompositionEnd, but the above fix is a significant improvement.
Please verify with a JP/CN/KR IME after updating.
📝 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.
| const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { | |
| if (e.key === "Enter" && !e[NEWLINE_MODIFIER_KEY]) { | |
| e.preventDefault(); | |
| onSubmit?.(); | |
| } | |
| onKeyDown?.(e); | |
| const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { | |
| onKeyDown?.(e); | |
| if (e.defaultPrevented || e.nativeEvent.isComposing) return; | |
| if (e.key === "Enter" && !e[NEWLINE_MODIFIER_KEY]) { | |
| e.preventDefault(); | |
| onSubmit?.(); | |
| } | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/chat/chat-toolbar.tsx` around lines 122 - 127, The handleKeyDown
currently calls onSubmit before onKeyDown and doesn't check IME composition
state; update handleKeyDown to invoke onKeyDown(e) first, then only call
onSubmit when Enter is pressed, the NEWLINE_MODIFIER_KEY is not held, and
composition is not in progress (check e.nativeEvent.isComposing or a tracked
composition flag). Reference the handleKeyDown function and the
onKeyDown/onSubmit handlers; optionally add onCompositionStart/onCompositionEnd
to track composition state for maximum robustness.
| timestamp: new Date(failMessage.createdAt).toLocaleTimeString([], { | ||
| hour: "2-digit", | ||
| minute: "2-digit", | ||
| }), | ||
| }, |
There was a problem hiding this comment.
Return a full timestamp here, not a display-formatted time.
components/chat-area.tsx Lines 71-74 parse message.timestamp, and Lines 320-347 use that value for ChatEventTime and date dividers. Returning only a localized time string breaks that contract for newly generated messages and can also shift the displayed time to the server's locale/timezone. The optimistic timestamp created in components/chat-area.tsx Lines 86-94 should use the same shape.
Suggested fix
- timestamp: new Date(failMessage.createdAt).toLocaleTimeString([], {
- hour: "2-digit",
- minute: "2-digit",
- }),
+ timestamp: failMessage.createdAt.toISOString(),
...
- timestamp: new Date(aiMessage.createdAt).toLocaleTimeString([], {
- hour: "2-digit",
- minute: "2-digit",
- }),
+ timestamp: aiMessage.createdAt.toISOString(),Also applies to: 151-155
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/actions/chat.ts` around lines 118 - 122, The timestamp returned for newly
generated messages (e.g., the failMessage object created in lib/actions/chat.ts)
is currently a localized time string; change it to return a full
machine-readable timestamp (ISO 8601 or numeric ms since epoch) matching the
shape parsed in components/chat-area.tsx so ChatEventTime and date dividers work
correctly—replace calls like new
Date(failMessage.createdAt).toLocaleTimeString(...) with new
Date(failMessage.createdAt).toISOString() (or Date.now()/createdAt as a number)
for both the failure path and the optimistic message path (the similar block
around lines 151-155) so the component receives the same timestamp shape it
expects.
| model Account { | ||
| id String @id | ||
| accountId String | ||
| providerId String | ||
| userId String | ||
| user User @relation(fields: [userId], references: [id], onDelete: Cascade) | ||
| accessToken String? | ||
| refreshToken String? | ||
| idToken String? | ||
| accessTokenExpiresAt DateTime? | ||
| refreshTokenExpiresAt DateTime? | ||
| scope String? | ||
| password String? | ||
| createdAt DateTime | ||
| updatedAt DateTime | ||
|
|
||
| @@map("account") | ||
| } |
There was a problem hiding this comment.
Enforce uniqueness for external account identities.
Without a composite uniqueness constraint on (providerId, accountId), the same provider account can be linked more than once and provider-based sign-in lookups become ambiguous.
Suggested change
model Account {
id String `@id`
accountId String
providerId String
userId String
user User `@relation`(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime
updatedAt DateTime
+ @@unique([providerId, accountId])
@@map("account")
}📝 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.
| model Account { | |
| id String @id | |
| accountId String | |
| providerId String | |
| userId String | |
| user User @relation(fields: [userId], references: [id], onDelete: Cascade) | |
| accessToken String? | |
| refreshToken String? | |
| idToken String? | |
| accessTokenExpiresAt DateTime? | |
| refreshTokenExpiresAt DateTime? | |
| scope String? | |
| password String? | |
| createdAt DateTime | |
| updatedAt DateTime | |
| @@map("account") | |
| } | |
| model Account { | |
| id String `@id` | |
| accountId String | |
| providerId String | |
| userId String | |
| user User `@relation`(fields: [userId], references: [id], onDelete: Cascade) | |
| accessToken String? | |
| refreshToken String? | |
| idToken String? | |
| accessTokenExpiresAt DateTime? | |
| refreshTokenExpiresAt DateTime? | |
| scope String? | |
| password String? | |
| createdAt DateTime | |
| updatedAt DateTime | |
| @@unique([providerId, accountId]) | |
| @@map("account") | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@prisma/schema.prisma` around lines 41 - 58, Add a composite unique constraint
on the Account model to prevent duplicate external identities by enforcing
uniqueness of (providerId, accountId); update the Prisma model named Account to
include a @@unique([providerId, accountId]) (or equivalent) so lookups and
relations that use providerId/accountId are deterministic and duplicates cannot
be created.
| userId String | ||
| user User @relation(fields: [userId], references: [id], onDelete: Cascade) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Expect to find a migration that updates/deletes legacy chat rows before the FK is enforced.
fd . prisma/migrations
rg -n 'guest|userId|ALTER TABLE.*Chat|REFERENCES.*user' prisma/schema.prisma prisma/migrations 2>/dev/nullRepository: PointerRAG/Project
Length of output: 720
🏁 Script executed:
# Check the full Chat model in schema.prisma to see if userId is optional or required
rg -B5 -A10 'model Chat' prisma/schema.prismaRepository: PointerRAG/Project
Length of output: 472
🏁 Script executed:
# Search for migration files in the repository (might be in different locations or have different extensions)
find . -type f \( -name "*migration*" -o -name "*.sql" -o -name "*.ts" \) | grep -i migrat | head -20Repository: PointerRAG/Project
Length of output: 115
🏁 Script executed:
# Search for "guest" references in the codebase to see if legacy data exists
rg -i 'guest' --type-list
rg -i '"guest"|'"'"'guest'"'"' -A2 -B2Repository: PointerRAG/Project
Length of output: 520
🏁 Script executed:
# Read the migration file to check if it contains backfill logic for Chat.userId
cat ./better-auth_migrations/2025-11-28T17-20-59.231Z.sqlRepository: PointerRAG/Project
Length of output: 1555
🏁 Script executed:
# Search for "guest" references in the codebase
rg 'guest' -iRepository: PointerRAG/Project
Length of output: 141
Verify the backfill before making Chat.userId a required FK.
Chat.userId currently defaults to "guest" (found in backend/core/models.py), but the schema change makes it a required foreign key referencing User.id. The migration file at ./better-auth_migrations/2025-11-28T17-20-59.231Z.sql does not include a backfill step to handle existing rows with the legacy "guest" value. This will cause migration failures unless existing chat records are deleted, reassigned to a real user, or a corresponding user row is created.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@prisma/schema.prisma` around lines 76 - 77, The migration makes Chat.userId
(Chat.userId) a required FK referencing User.id without backfilling rows that
default to "guest"; update the migration to ensure referential integrity before
adding the constraint by either creating a corresponding User record with id =
"guest" (and sensible metadata) or updating all existing Chat rows to point to
an existing real user id, then add the FK constraint; also remove or adjust the
default "guest" in the application model (backend/core/models.py) to avoid
reintroducing invalid values.
…smatch, fix issues in code reviews
No description provided.