-
Notifications
You must be signed in to change notification settings - Fork 88
New playground search experience #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughRefactors the Playground into separate Chat and Search experiences, overhauls the Search client into a Chunk Explorer with reranking controls, updates the server search router to use vector-store querying with rerank options, restructures the Next.js config to an async factory using jiti, updates the sidebar links, and tweaks chat settings persistence. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant C as ChunkExplorerPageClient
participant API as searchRouter
participant E as embeddingModel
participant VS as vectorStore
participant R as reranker (optional)
U->>C: Enter query + options (topK, rerank, model, limit, filter)
C->>API: search({ namespaceId, query, topK, rerank, rerankModel, rerankLimit, filter })
par Resolve dependencies
API->>E: getEmbeddingModel()
API->>VS: getVectorStore()
end
API->>VS: queryVectorStore({ query, topK, includeMetadata, filter })
alt rerank == true
API->>R: rerank(results, model=rerankModel, limit=rerankLimit)
R-->>API: reranked results
else
Note over API,VS: Use raw vector-store results
end
API-->>C: results[]
C-->>U: Render results, counts, states
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (8)
apps/web/src/components/app-sidebar/links.ts (1)
12-12
: Nit: Drop unused import (if not planned).
SearchIcon
is imported but not used. If there's no immediate use, remove it to keep the bundle and lints clean.apps/web/src/server/api/routers/search.ts (3)
88-97
: GuardrerankLimit
againsttopK
.If
rerankLimit
>topK
, you’ll over-request/rerank more than you retrieve. Clamp it totopK
.- rerankLimit: input.rerankLimit, + rerankLimit: + input.rerankLimit !== undefined + ? Math.min(input.rerankLimit, input.topK) + : undefined,
118-120
: Usage accounting parity with agentic search.You increment by
1
here, while thesearch
route usesresults.totalQueries
. If billing/quotas depend on actual underlying calls (rerank, pagination), consider incrementing by the real query count returned by the vector/keyword layer.
121-133
: Return the applied filter for transparency.If you convert the filter for keyword mode, consider returning the applied (string) filter rather than the original object to aid debugging and reproducibility.
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (4)
86-88
: Type-safety for handleFilterChangeAvoid any to catch mistakes at compile time.
-const handleFilterChange = (key: keyof ChunkExplorerFilters, value: any) => { - setFilters((prev) => ({ ...prev, [key]: value })); -}; +const handleFilterChange = <K extends keyof ChunkExplorerFilters>( + key: K, + value: ChunkExplorerFilters[K], +) => { + setFilters((prev) => ({ ...prev, [key]: value })); +};
160-174
: Use functional state update for togglePrevents stale state bugs if multiple rapid clicks occur.
-onClick={() => setShowFilters(!showFilters)} +onClick={() => setShowFilters((v) => !v)}
284-293
: Friendlier error handlingTRPC errors can be verbose. Consider a safe fallback while still exposing details in logs.
-<p className="text-muted-foreground mt-1 text-sm"> - {error.message} -</p> +<p className="text-muted-foreground mt-1 text-sm"> + {(error as { message?: string })?.message ?? "Something went wrong. Please try again."} +</p>
90-368
: Optional: persist query + filters in the URLImproves shareability and back/forward navigation. Encode state into searchParams and initialize from them on mount.
Happy to draft the useSearchParams + router.replace integration.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/page.tsx
(1 hunks)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
(3 hunks)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.tsx
(1 hunks)apps/web/src/components/app-sidebar/links.ts
(2 hunks)apps/web/src/server/api/routers/search.ts
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/src/server/api/routers/search.ts (4)
apps/web/src/server/api/trpc.ts (1)
protectedProcedure
(122-133)apps/web/src/server/api/auth.ts (1)
getNamespaceByUser
(5-25)packages/engine/src/keyword-store/index.ts (1)
KeywordStore
(50-247)apps/web/src/lib/api/usage.ts (1)
incrementSearchUsage
(5-23)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.tsx (2)
apps/web/src/components/dashboard-page-wrapper/index.tsx (1)
DashboardPageWrapper
(21-92)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (1)
ChunkExplorerPageClient
(45-369)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (3)
apps/web/src/hooks/use-namespace.ts (1)
useNamespace
(9-46)packages/engine/src/vector-store/pinecone.ts (1)
query
(97-114)apps/web/src/lib/analytics.ts (1)
logEvent
(7-13)
🔇 Additional comments (4)
apps/web/src/components/app-sidebar/links.ts (1)
91-104
: Verify active-state behavior for nested links."Chat" is marked
exact: true
; "Chunk Explorer" is not. Confirm the sidebar correctly highlights the active item on/playground/search
and does not incorrectly keep the group or "Chat" highlighted.apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/page.tsx (1)
11-17
: LGTM: Chat-only playground page.Title and actions match the new navigation model. No issues spotted.
If legacy links used
?tab=search
, consider a redirect to/playground/search
to avoid broken bookmarks.apps/web/src/server/api/routers/search.ts (1)
6-6
: zod/v4 imports are consistent repo-wide. No directimport "zod"
entries found; all code imports from"zod/v4"
. Transitive dependencies still include v3.x, but project code is fully on v4.apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.tsx (1)
5-9
: LGTM: clean page compositionServer page wraps the client with DashboardPageWrapper and requireNamespace=true. No issues spotted.
<div className="flex gap-2"> | ||
<Input | ||
value={query} | ||
onChange={(e) => setQuery(e.target.value)} | ||
placeholder="Enter your search query to explore chunks..." | ||
className="flex-1" | ||
/> | ||
<Button | ||
type="submit" | ||
disabled={isLoading || !query.trim()} | ||
isLoading={isLoading || isFetching} | ||
> | ||
<SearchIcon className="size-4" /> | ||
Explore | ||
</Button> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A11y: add accessible name for the search input
Input has no programmatic label; placeholders aren’t labels. Add a visually hidden label and an id.
-<div className="flex gap-2">
+<div className="flex gap-2">
+ <Label htmlFor="chunkQuery" className="sr-only">
+ Search chunks
+ </Label>
<Input
+ id="chunkQuery"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Enter your search query to explore chunks..."
className="flex-1"
/>
📝 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.
<div className="flex gap-2"> | |
<Input | |
value={query} | |
onChange={(e) => setQuery(e.target.value)} | |
placeholder="Enter your search query to explore chunks..." | |
className="flex-1" | |
/> | |
<Button | |
type="submit" | |
disabled={isLoading || !query.trim()} | |
isLoading={isLoading || isFetching} | |
> | |
<SearchIcon className="size-4" /> | |
Explore | |
</Button> | |
</div> | |
<div className="flex gap-2"> | |
<Label htmlFor="chunkQuery" className="sr-only"> | |
Search chunks | |
</Label> | |
<Input | |
id="chunkQuery" | |
value={query} | |
onChange={(e) => setQuery(e.target.value)} | |
placeholder="Enter your search query to explore chunks..." | |
className="flex-1" | |
/> | |
<Button | |
type="submit" | |
disabled={isLoading || !query.trim()} | |
isLoading={isLoading || isFetching} | |
> | |
<SearchIcon className="size-4" /> | |
Explore | |
</Button> | |
</div> |
🤖 Prompt for AI Agents
In
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
around lines 96 to 111, the Input has no programmatic label which breaks
accessibility; add an id prop to the Input and render a visually hidden label
element tied to that id (e.g., <label htmlFor="search-query"
className="sr-only">Search query</label>) immediately before the Input so
assistive tech can announce it; ensure the Input component forwards the id to
the underlying input and keep the placeholder for hinting only.
...src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
Outdated
Show resolved
Hide resolved
...src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
Outdated
Show resolved
Hide resolved
...src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/web/src/server/api/routers/search.ts (1)
16-27
: Discriminate schema by mode; fix filter shape for keyword search.KeywordStore.search expects an OData filter string, but the schema sets
filter
as an object for all modes. Use a discriminated union sosemantic
accepts an object andkeyword
accepts a string. Partially addressed earlier; schema still conflates types.-const chunkExplorerInputSchema = z.object({ - namespaceId: z.string(), - query: z.string().min(1, "Query is required"), - mode: z.enum(["semantic", "keyword"]).default("semantic"), - topK: z.number().min(1).max(100).default(20), - minScore: z.number().min(0).max(1).optional(), - rerank: z.boolean().default(true), - rerankLimit: z.number().min(1).max(100).optional(), - filter: z.record(z.string(), z.any()).optional(), - includeMetadata: z.boolean().default(true), - includeRelationships: z.boolean().default(false), -}); +const semanticSchema = z.object({ + namespaceId: z.string(), + query: z.string().min(1, "Query is required"), + mode: z.literal("semantic"), + topK: z.number().min(1).max(100).default(20), + minScore: z.number().min(0).max(1).optional(), + rerank: z.boolean().default(true), + rerankLimit: z.number().min(1).max(100).optional(), + filter: z.record(z.string(), z.any()).optional(), // structured metadata filter + includeMetadata: z.boolean().default(true), + includeRelationships: z.boolean().default(false), +}); + +const keywordSchema = z.object({ + namespaceId: z.string(), + query: z.string().min(1, "Query is required"), + mode: z.literal("keyword"), + topK: z.number().min(1).max(100).default(20), + minScore: z.number().min(0).max(1).optional(), + filter: z.string().optional(), // OData filter string + includeMetadata: z.boolean().default(true), + includeRelationships: z.boolean().default(false), +}); + +const chunkExplorerInputSchema = z.discriminatedUnion("mode", [ + semanticSchema, + keywordSchema, +]);
🧹 Nitpick comments (3)
apps/web/src/server/api/routers/search.ts (3)
87-90
: Tighten types and remove unused var.
results
is always assigned to an array; drop| undefined
.queryPerformed
is redundant (always equalsinput.query
).-let results: QueryVectorStoreResult["results"] | undefined = []; -let queryPerformed = input.query; +let results: QueryVectorStoreResult["results"] = [];And in the return payload, use
query: input.query
.
122-124
: Usage tracking nuance.You always increment by 1. If semantic + rerank triggers additional retrieval/rerank passes you may want to track a more representative counter (e.g.,
totalQueries
if exposed byqueryVectorStore
). If not available, leave as-is.
125-137
: Return applied vs requested filter for observability.Right now you echo
input.filter
(object) even when keyword mode applies an OData string. Return both the requested and the applied forms for easier debugging.- return { - results, - query: queryPerformed, + return { + results, + query: input.query, mode: input.mode, totalResults: results.length, parameters: { topK: input.topK, minScore: input.minScore, rerank: input.rerank, rerankLimit: input.rerankLimit, - filter: input.filter, + requestedFilter: input.filter, + appliedFilter: typeof odataFilter === "string" ? odataFilter : undefined, }, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/src/server/api/routers/search.ts
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/server/api/routers/search.ts (5)
apps/web/src/server/api/trpc.ts (1)
protectedProcedure
(122-133)apps/web/src/server/api/auth.ts (1)
getNamespaceByUser
(5-25)packages/engine/src/vector-store/parse.ts (1)
QueryVectorStoreResult
(133-135)packages/engine/src/keyword-store/index.ts (1)
KeywordStore
(50-247)apps/web/src/lib/api/usage.ts (1)
incrementSearchUsage
(5-23)
🔇 Additional comments (2)
apps/web/src/server/api/routers/search.ts (2)
80-85
: Good guard: enforce keyword mode availability.Rejecting keyword queries when
namespace.keywordEnabled
is false is correct and prevents confusing UX.
109-117
: Convert and apply filters for keyword search and pass tenant ID
- In
exploreChunks
, map theinput.filter
object to an OData string and supply it tokeywordStore.search(..., { filter: odataFilter })
.- Extend the
KeywordStore
constructor call to include the tenant/org identifier (e.g.new KeywordStore(namespace.id, namespace.organizationId ?? ctx.session.org?.id)
).- Confirm which tenant property is correct and whether you need to support more complex filters (ranges, arrays) in the converter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (4)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (4)
91-106
: A11y: Add accessible label for search input.The search input lacks a programmatic label; placeholders are not accessible substitutes. This was flagged in a previous review and remains unaddressed.
Apply this diff to add a visually hidden label:
<div className="flex gap-2"> + <Label htmlFor="chunkQuery" className="sr-only"> + Search chunks + </Label> <Input + id="chunkQuery" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Enter your search query to explore chunks..." className="flex-1" />
112-129
: A11y: Bind "Mode" label to the Select trigger.The
htmlFor="mode"
attribute points to no element. This was flagged in a previous review and remains unaddressed.Apply this diff to properly associate the label:
-<Label htmlFor="mode" className="text-sm"> +<Label id="mode-label" className="text-sm"> Mode: </Label> <Select value={mode} onValueChange={(value: "semantic" | "keyword") => setMode(value) } > - <SelectTrigger className="w-32"> + <SelectTrigger className="w-32" aria-labelledby="mode-label"> <SelectValue /> </SelectTrigger>
115-119
: Auto-disable rerank when switching to keyword mode.When switching to keyword mode,
rerank
andrerankLimit
should be cleared to avoid sending unsupported parameters. This was flagged in a previous review and remains unaddressed.Apply this diff to reset rerank parameters:
<Select value={mode} - onValueChange={(value: "semantic" | "keyword") => - setMode(value) - } + onValueChange={(value: "semantic" | "keyword") => { + setMode(value); + if (value === "keyword") { + setRerank(false); + setRerankLimit(undefined); + } + }} >
134-148
: Clamp rerankLimit when topK decreases.When
topK
is lowered,rerankLimit
can exceed the newtopK
value, leading to invalid state. This was flagged in a previous review and remains unaddressed.Apply this diff to clamp
rerankLimit
:<Select value={topK.toString()} - onValueChange={(value) => setTopK(parseInt(value))} + onValueChange={(value) => { + const nextTopK = parseInt(value, 10); + setTopK(nextTopK); + if (rerankLimit != null && rerankLimit > nextTopK) { + setRerankLimit(nextTopK); + } + }} >
🧹 Nitpick comments (2)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (2)
266-273
: Add explicit type for search result.The
result
parameter is typed asany
, bypassing type safety. Consider defining a proper type or importing it from the tRPC router output.If the type is available from tRPC:
+import type { RouterOutputs } from "@/trpc/react"; + +type SearchResult = RouterOutputs["search"]["exploreChunks"]["results"][number];Then use it in the map:
-data.results.map((result: any) => ( +data.results.map((result: SearchResult) => ( <SearchChunkAlternatively, inline a minimal type:
-data.results.map((result: any) => ( +data.results.map((result: { id: string }) => ( <SearchChunk
223-233
: Consider safer error message extraction.Direct access to
error.message
assumes a specific error shape. tRPC errors can have nested structures or missing message properties in edge cases.Apply this diff for defensive error handling:
<div className="text-center text-red-600"> <p className="font-medium">Error occurred</p> <p className="text-muted-foreground mt-1 text-sm"> - {error.message} + {error?.message || "An unexpected error occurred"} </p> </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
apps/web/next.config.ts
(1 hunks)apps/web/package.json
(2 hunks)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
(3 hunks)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.tsx
(1 hunks)apps/web/src/components/app-sidebar/links.ts
(2 hunks)packages/db/prisma/schema/schema.prisma
(1 hunks)packages/db/src/client.ts
(1 hunks)packages/db/src/index.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.tsx
- apps/web/src/components/app-sidebar/links.ts
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (3)
apps/web/src/hooks/use-namespace.ts (1)
useNamespace
(9-46)packages/engine/src/vector-store/pinecone.ts (1)
query
(97-114)apps/web/src/lib/analytics.ts (1)
logEvent
(7-13)
apps/web/next.config.ts (1)
apps/web/src/middleware.ts (1)
config
(9-21)
🔇 Additional comments (2)
packages/db/src/client.ts (1)
6-6
: LGTM!The import path correctly reflects the updated Prisma generator output location.
packages/db/src/index.ts (1)
4-4
: LGTM!The re-export path correctly reflects the updated Prisma generator output location, ensuring consumers access the generated client from the new path.
...src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
Outdated
Show resolved
Hide resolved
...src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
Outdated
Show resolved
Hide resolved
…eat/new-playground-search
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
apps/web/package.json (1)
52-52
: Fix the invalidjiti
range.
jiti@^2.6.1
still isn’t published, so installs will fail until a real version ships. Please pin to the latest released build (2.6.0) or otherwise pick a valid range.- "jiti": "^2.6.1", + "jiti": "^2.6.0",apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (2)
109-122
: Clamp rerankLimit when topK changes.When topK is reduced, rerankLimit could exceed the new topK value, causing inconsistent UI state. The previous review comment on lines 146-166 already flagged this issue.
Apply this diff to clamp rerankLimit:
<Select value={topK.toString()} - onValueChange={(value) => setTopK(parseInt(value))} + onValueChange={(value) => { + const newTopK = parseInt(value, 10); + setTopK(newTopK); + if (rerankLimit > newTopK) { + setRerankLimit(newTopK); + } + }} >
148-165
: Guard against out-of-range rerankLimit display.The displayed value
(rerankLimit || topK)
can show a value not present in the dropdown options if topK was reduced after rerankLimit was set. A previous review comment already flagged this issue.Apply this diff to clamp the display value:
<Select - value={(rerankLimit || topK).toString()} + value={Math.min(rerankLimit || topK, topK).toString()} onValueChange={(value) => setRerankLimit(parseInt(value, 10))} >
🧹 Nitpick comments (5)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (4)
33-40
: Move interface to end of file per coding guidelines.The coding guidelines specify: "Place static content (consts) and TypeScript interfaces at the end of the file" for files matching
apps/web/**/*.{ts,tsx}
. Move theChunkExplorerFilters
interface after the component definition.Note: A previous review comment already flagged this interface as unused. Consider removing it entirely if it's not needed, or move it to the end if you plan to use it later.
As per coding guidelines.
111-111
: Add radix parameter to parseInt.The
parseInt
call is missing the radix parameter. Always specify radix 10 to avoid potential parsing issues.Apply this diff:
- onValueChange={(value) => setTopK(parseInt(value))} + onValueChange={(value) => setTopK(parseInt(value, 10))}
150-150
: Add radix parameter to parseInt.The
parseInt
call is missing the radix parameter. Always specify radix 10 to avoid potential parsing issues.Apply this diff:
- onValueChange={(value) => setRerankLimit(parseInt(value))} + onValueChange={(value) => setRerankLimit(parseInt(value, 10))}
211-211
: Avoid usingany
type.The
result
parameter uses theany
type, which bypasses TypeScript's type checking. Import or define a proper type for the search result.If a
SearchResult
type is available from the API response, use it:- data.results.map((result: any) => ( + data.results.map((result) => (The type should be inferred from the
data.results
array. If the inference isn't working, define a proper interface for the result type instead of usingany
.apps/web/src/server/api/routers/search.ts (1)
57-61
: Consider includingunorderedIds
in the response.The
queryVectorStore
function returnsunorderedIds
(original IDs before reranking), which could be useful for debugging or understanding whether reranking was applied. Consider including it in the response.Apply this diff:
return { results: queryResult.results, query: queryResult.query, totalResults: queryResult.results.length, + unorderedIds: queryResult.unorderedIds, };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
apps/web/package.json
(1 hunks)apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
(3 hunks)apps/web/src/components/chat/chat-settings.store.ts
(2 hunks)apps/web/src/server/api/routers/search.ts
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.{ts,tsx}
: Place static content (consts) and TypeScript interfaces at the end of the file
Minimize use of useEffect and setState
Use Zod for form validation
Minimize client-side state
Prefer server state when possibleImport UI components from the @agentset/ui alias instead of relative paths
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/components/chat/chat-settings.store.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for file names (e.g., auth-provider.tsx, user-settings.ts)
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/components/chat/chat-settings.store.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use package import aliases (e.g., @agentset/ui) and avoid relative paths into other packages
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/components/chat/chat-settings.store.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
apps/web/src/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Organize the web app by feature, then by type under src/{app,components,lib,hooks,types,services}
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/components/chat/chat-settings.store.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
{apps/web,packages/*,tooling/*}/package.json
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
{apps/web,packages/*,tooling/*}/package.json
: Reference internal packages with version "workspace:" in package.json dependencies (e.g., @agentset/)
Use "catalog:" references for shared external dependencies
Avoid duplicate external dependencies across packages by using shared catalog entries
Pin AI SDK versions for consistency
Use exact versions for critical dependencies
Files:
apps/web/package.json
apps/web/package.json
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
The web app should build using Next.js (e.g., next build in the build script)
Files:
apps/web/package.json
🧠 Learnings (1)
📚 Learning: 2025-10-06T12:02:55.630Z
Learnt from: CR
PR: agentset-ai/agentset#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-10-06T12:02:55.630Z
Learning: Applies to {apps/web,packages/*,tooling/*}/package.json : Pin AI SDK versions for consistency
Applied to files:
apps/web/package.json
🧬 Code graph analysis (2)
apps/web/src/server/api/routers/search.ts (5)
packages/validation/src/re-ranker/index.ts (1)
rerankerSchema
(5-12)packages/engine/src/embedding/index.ts (1)
getNamespaceEmbeddingModel
(9-88)packages/engine/src/vector-store/index.ts (1)
getNamespaceVectorStore
(6-75)packages/engine/src/vector-store/query.ts (1)
queryVectorStore
(15-55)apps/web/src/lib/api/usage.ts (1)
incrementSearchUsage
(5-23)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (4)
packages/validation/src/re-ranker/constants.ts (2)
RerankingModel
(20-20)DEFAULT_RERANKER
(22-22)apps/web/src/hooks/use-namespace.ts (1)
useNamespace
(9-46)apps/web/src/lib/analytics.ts (1)
logEvent
(7-13)apps/web/src/components/reranker-selector.tsx (1)
RerankerSelector
(19-48)
🔇 Additional comments (3)
apps/web/src/components/chat/chat-settings.store.ts (2)
52-52
: LGTM! Defensive spread ensures complete default values.Spreading
defaultState
first guarantees that all fields are present in the namespace state object, even if the persisted state is missing newly added fields. This prevents runtime errors when thedefaultState
schema evolves.
61-61
: LGTM! Good cleanup removing unused parameter.The
get
parameter was unused since bothsetSettings
andreset
receive the current state through theset
callback. Removing it improves code clarity.apps/web/src/server/api/routers/search.ts (1)
22-22
: Previous filter type issue resolved.The previous review flagged a critical issue regarding the filter type mismatch between keyword (OData string) and semantic (object) modes. This issue has been resolved by removing keyword mode entirely and using only vector store-based search with an object filter, which is the appropriate type for vector stores like Pinecone and Turbopuffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (5)
apps/web/src/server/api/routers/search.ts (2)
15-23
: Previous review comment remains unaddressed: schema placement and missing parameters.The issues flagged in the previous review have not been resolved:
- The schema violates the coding guideline requiring static content to be placed at the end of the file
- Missing optional parameters:
minScore
,includeMetadata
,includeRelationships
- Missing default values for
topK
,rerank
, and proper optional flag forrerankLimit
As per coding guidelines.
42-52
: Previous review comment remains unaddressed: hardcoded values instead of schema parameters.The
includeMetadata
is still hardcoded totrue
, andminScore
andincludeRelationships
are still not passed toqueryVectorStore
. Once the schema is updated per the previous comment, these should be passed frominput
.apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (3)
81-86
: Add accessible label for search input.The search input lacks a programmatic label, which is required for assistive technologies. Placeholders are not substitutes for labels.
This issue was previously flagged. Please refer to the earlier review comment for the suggested fix (adding a visually hidden label with
htmlFor
binding).
100-113
: Clamp rerankLimit when topK decreases.When users lower topK, the current rerankLimit value can exceed the new topK, leading to invalid UI state and potential backend errors.
This issue was previously flagged. Update the
onValueChange
handler to clamprerankLimit
to[1, topK]
whenevertopK
changes:<Select value={topK.toString()} - onValueChange={(value) => setTopK(parseInt(value))} + onValueChange={(value) => { + const nextTopK = parseInt(value, 10); + setTopK(nextTopK); + if (rerankLimit > nextTopK) { + setRerankLimit(nextTopK); + } + }} >
137-157
: Bind "Rerank Limit" label to the Select and guard display value.Two issues:
- The label is not programmatically associated with the Select.
- The displayed value
(rerankLimit || topK).toString()
can exceedtopK
iftopK
was lowered afterrerankLimit
was set, causing the Select to show an invalid value.Apply this fix:
-<Label className="text-sm">Rerank Limit:</Label> +<Label id="rerank-limit-label" className="text-sm">Rerank Limit:</Label> <Select - value={(rerankLimit || topK).toString()} + value={Math.min(rerankLimit ?? topK, topK).toString()} onValueChange={(value) => setRerankLimit(parseInt(value))} > - <SelectTrigger className="w-20"> + <SelectTrigger className="w-20" aria-labelledby="rerank-limit-label"> <SelectValue /> </SelectTrigger>The display-value issue was previously flagged in an earlier review.
🧹 Nitpick comments (1)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (1)
102-102
: Specify radix in parseInt calls.Omitting the radix can lead to unexpected parsing behavior in edge cases (e.g., leading zeros interpreted as octal in older environments).
Apply this diff:
-onValueChange={(value) => setTopK(parseInt(value))} +onValueChange={(value) => setTopK(parseInt(value, 10))} -onValueChange={(value) => setRerankLimit(parseInt(value))} +onValueChange={(value) => setRerankLimit(parseInt(value, 10))}Also applies to: 141-141
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
(2 hunks)apps/web/src/server/api/routers/search.ts
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.{ts,tsx}
: Place static content (consts) and TypeScript interfaces at the end of the file
Minimize use of useEffect and setState
Use Zod for form validation
Minimize client-side state
Prefer server state when possibleImport UI components from the @agentset/ui alias instead of relative paths
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for file names (e.g., auth-provider.tsx, user-settings.ts)
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use package import aliases (e.g., @agentset/ui) and avoid relative paths into other packages
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
apps/web/src/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Organize the web app by feature, then by type under src/{app,components,lib,hooks,types,services}
Files:
apps/web/src/server/api/routers/search.ts
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
🧬 Code graph analysis (2)
apps/web/src/server/api/routers/search.ts (6)
packages/validation/src/re-ranker/index.ts (1)
rerankerSchema
(5-12)apps/web/src/server/api/trpc.ts (1)
protectedProcedure
(122-133)packages/engine/src/embedding/index.ts (1)
getNamespaceEmbeddingModel
(9-88)packages/engine/src/vector-store/index.ts (1)
getNamespaceVectorStore
(6-75)packages/engine/src/vector-store/query.ts (1)
queryVectorStore
(15-55)apps/web/src/lib/api/usage.ts (1)
incrementSearchUsage
(5-23)
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx (5)
apps/web/src/hooks/use-namespace.ts (1)
useNamespace
(9-46)packages/validation/src/re-ranker/constants.ts (2)
RerankingModel
(20-20)DEFAULT_RERANKER
(22-22)apps/web/src/lib/analytics.ts (1)
logEvent
(7-13)apps/web/src/components/reranker-selector.tsx (1)
RerankerSelector
(19-48)packages/ui/src/components/data-wrapper.tsx (1)
DataWrapper
(6-31)
<div className="flex items-center gap-2"> | ||
<Label className="text-sm">Results:</Label> | ||
<Select | ||
value={topK.toString()} | ||
onValueChange={(value) => setTopK(parseInt(value))} | ||
> | ||
<SelectTrigger className="w-20"> | ||
<SelectValue /> | ||
</SelectTrigger> | ||
<SelectContent> | ||
<SelectItem value="10">10</SelectItem> | ||
<SelectItem value="20">20</SelectItem> | ||
<SelectItem value="50">50</SelectItem> | ||
<SelectItem value="100">100</SelectItem> | ||
</SelectContent> | ||
</Select> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bind "Results" label to the Select control.
The label is not programmatically associated with the Select trigger, breaking assistive technology navigation.
Add an id
to the Label and link it to the SelectTrigger via aria-labelledby
:
-<Label className="text-sm">Results:</Label>
+<Label id="results-label" className="text-sm">Results:</Label>
<Select
value={topK.toString()}
onValueChange={(value) => setTopK(parseInt(value))}
>
- <SelectTrigger className="w-20">
+ <SelectTrigger className="w-20" aria-labelledby="results-label">
<SelectValue />
</SelectTrigger>
📝 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.
<div className="flex items-center gap-2"> | |
<Label className="text-sm">Results:</Label> | |
<Select | |
value={topK.toString()} | |
onValueChange={(value) => setTopK(parseInt(value))} | |
> | |
<SelectTrigger className="w-20"> | |
<SelectValue /> | |
</SelectTrigger> | |
<SelectContent> | |
<SelectItem value="10">10</SelectItem> | |
<SelectItem value="20">20</SelectItem> | |
<SelectItem value="50">50</SelectItem> | |
<SelectItem value="100">100</SelectItem> | |
</SelectContent> | |
</Select> | |
<div className="flex items-center gap-2"> | |
<Label id="results-label" className="text-sm">Results:</Label> | |
<Select | |
value={topK.toString()} | |
onValueChange={(value) => setTopK(parseInt(value))} | |
> | |
<SelectTrigger className="w-20" aria-labelledby="results-label"> | |
<SelectValue /> | |
</SelectTrigger> | |
<SelectContent> | |
<SelectItem value="10">10</SelectItem> | |
<SelectItem value="20">20</SelectItem> | |
<SelectItem value="50">50</SelectItem> | |
<SelectItem value="100">100</SelectItem> | |
</SelectContent> | |
</Select> |
🤖 Prompt for AI Agents
In
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
around lines 98 to 113, the "Results" Label is not programmatically associated
with the Select control which breaks accessibility; add an id to the Label (e.g.
results-label) and reference that id from the SelectTrigger via aria-labelledby
so screen readers and assistive tech can associate the label with the select
trigger; ensure the id is unique in the component and keep the same string on
both elements.
<div className="flex items-center gap-2"> | ||
<Label className="text-sm">Model:</Label> | ||
<RerankerSelector | ||
value={rerankModel} | ||
onValueChange={setRerankModel} | ||
/> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bind "Model" label to the RerankerSelector.
The label is not programmatically associated with the selector, which affects assistive technology users.
Add an id
to the Label and pass aria-labelledby
to RerankerSelector (ensure the component forwards it to its SelectTrigger):
-<Label className="text-sm">Model:</Label>
+<Label id="rerank-model-label" className="text-sm">Model:</Label>
<RerankerSelector
value={rerankModel}
onValueChange={setRerankModel}
+ aria-labelledby="rerank-model-label"
/>
Note: Verify that RerankerSelector
forwards aria-labelledby
to its underlying SelectTrigger
.
📝 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.
<div className="flex items-center gap-2"> | |
<Label className="text-sm">Model:</Label> | |
<RerankerSelector | |
value={rerankModel} | |
onValueChange={setRerankModel} | |
/> | |
</div> | |
<div className="flex items-center gap-2"> | |
<Label id="rerank-model-label" className="text-sm">Model:</Label> | |
<RerankerSelector | |
value={rerankModel} | |
onValueChange={setRerankModel} | |
aria-labelledby="rerank-model-label" | |
/> | |
</div> |
🤖 Prompt for AI Agents
In
apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/playground/search/page.client.tsx
around lines 129 to 135, the "Model" Label is not programmatically associated
with the RerankerSelector; add an id attribute to the Label (e.g.
id="reranker-label") and pass aria-labelledby="reranker-label" into the
RerankerSelector props so it is announced by assistive tech; also update the
RerankerSelector component to accept and forward the aria-labelledby prop to its
underlying SelectTrigger (or root select element) so the association is
preserved end-to-end.
Note
Adds a new Chunk Explorer with semantic/keyword modes and advanced filters, updates Playground navigation/UI, and introduces a backend endpoint to power chunk exploration.
Chat
and remove in-page tabs.playground/search
) with advanced filters (mode, topK, minScore, rerank, metadata, relationships), improved loading/error states, and detailed results header.Playground
now has nested items forChat
andChunk Explorer
.trpc.search.exploreChunks
with filter params; analytics event renamed toplayground_chunk_exploration
.search.exploreChunks
TRPC endpoint with validated input, supports semantic (vector store + optional rerank) and keyword search, enforceskeywordEnabled
, returns results with parameters/metadata, and tracks usage.Written by Cursor Bugbot for commit a1b4b17. This will update automatically on new commits. Configure here.
Summary by CodeRabbit