-
Notifications
You must be signed in to change notification settings - Fork 151
Improved agentic chat #74
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
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughRefactors agentic search from multi-round query orchestration to a tool-based streaming architecture, simplifies routes to use vector-store results directly, changes citation IDs to string refs, removes legacy logging/status UI and utilities, and updates prompts/types/LLM defaults to align with the new tool flow. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Route as Chat/Hosting Route
participant Agentic as streamAgenticResponse
participant Tools as Agentic Tools
participant VectorStore
participant LLM
Client->>Route: POST messages (embeddingModel, vectorStore, topK, rerank)
Route->>Agentic: streamAgenticResponse(options)
Note over Agentic,Tools: initialize tools (semantic_search, keyword_search?, expand)
Agentic->>LLM: streamText(messages, systemPrompt, model, tools, temperature)
loop Agent reasoning / steps
LLM->>Tools: invoke tool (semantic_search / keyword_search / expand)
Tools->>VectorStore: query (embeddingModel, query, topK, rerank)
VectorStore-->>Tools: return results (chunks, metadata)
Tools-->>LLM: formatted source block
LLM-->>Agentic: step event (may include searchResults)
Agentic->>Agentic: accumulate totalQueries/onStepFinish
end
LLM->>Agentic: finish
Agentic->>Agentic: call afterQueries(totalQueries)
Agentic-->>Route: StreamingResult (text, searchResults)
Route->>Client: toUIMessageStreamResponse (streamed UI message)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (3)apps/web/**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
Files:
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}📄 CodeRabbit inference engine (.cursor/rules/shadcn.mdc)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Files:
🔇 Additional comments (6)
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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/src/components/chat/message-actions/index.tsx (1)
67-74: Memoization logic ignoresmessageprop changes.The custom comparison function only checks
isLoading, which means the component won't re-render when themessageprop changes (e.g., content updates, new parts, or ID changes). This can result in stale UI where action buttons don't reflect the current message state.🔎 Proposed fix to compare message ID
export const MessageActions = memo( PureMessageActions, (prevProps, nextProps) => { if (prevProps.isLoading !== nextProps.isLoading) return false; + if (prevProps.message.id !== nextProps.message.id) return false; return true; }, );Alternatively, remove the custom comparison function to let React compare all props:
-export const MessageActions = memo( - PureMessageActions, - (prevProps, nextProps) => { - if (prevProps.isLoading !== nextProps.isLoading) return false; - - return true; - }, -); +export const MessageActions = memo(PureMessageActions);apps/web/src/components/chat/citation-modal.tsx (1)
20-53: Conditional hook call violates Rules of Hooks.Lines 26-28 call
useHosting()after a conditional return, which breaks React's Rules of Hooks. While the comment claimsisHostingwon't change, this pattern can cause subtle bugs and will trigger ESLint warnings witheslint-plugin-react-hooks.Consider restructuring to call hooks unconditionally:
🔎 Proposed fix
const useHostingCitationName = ({ source, }: Pick<CitationModalProps, "source">) => { const isHosting = useIsHosting(); + const hosting = useHosting(); - // it's fine to call hooks conditionally here because we'll never get a different value for `isHosting` within the same render - if (!isHosting) return null; - - const hosting = useHosting(); const citationName = useMemo(() => { + if (!isHosting) return null; if (!hosting.citationMetadataPath || !source.metadata) return null; const path = hosting.citationMetadataPath.split("."); let value: unknown = source.metadata; for (const key of path) { if ( value === null || typeof value !== "object" || typeof value === "undefined" ) return null; value = (value as Record<string, unknown>)[key]; } if (typeof value === "string") return value; if (typeof value === "number") return value.toString(); if (typeof value === "boolean") return value ? "True" : "False"; return null; - }, [hosting, source.metadata]); + }, [isHosting, hosting, source.metadata]); - return citationName ? citationName : `[Source]`; + if (!isHosting) return null; + return citationName ?? `[Source]`; };
🧹 Nitpick comments (8)
apps/web/src/lib/prompts.ts (1)
55-59: Consider including the source ID in the formatted output.The
formatSourcesfunction uses numeric indices (<source_1>,<source_2>) but the citation format in the prompt expects actual chunk IDs like[ref:x86jecs6fauhiwn4rua4nyjk#...]. Including the source ID in the XML tags would help the LLM generate correct citations.🔎 Proposed enhancement
export const formatSources = (sources: QueryVectorStoreResult["results"]) => { return sources - .map((s, idx) => `<source_${idx + 1}>\n${s.text}\n</source_${idx + 1}>`) + .map((s) => `<source id="${s.id}">\n${s.text}\n</source>`) .join("\n\n"); };apps/web/src/app/api/(internal-api)/hosting-search/route.ts (1)
52-53: UnusedkeywordEnabledfield being queried.The
namespace.keywordEnabledfield is selected but no longer used since the search mode is now hardcoded to"semantic". Consider removing it from the select clause to reduce query payload.🔎 Proposed fix
namespace: { select: { id: true, vectorStoreConfig: true, embeddingConfig: true, - keywordEnabled: true, }, },apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx (1)
121-142: SearchSkeleton shows outdated "Queries performed" UI.The skeleton still displays "Queries performed:" but the actual results no longer show a queries list (removed in this PR). The skeleton should be updated to match the current UI structure.
🔎 Proposed fix
const SearchSkeleton = () => { return ( <div className="w-full"> - <div className="w-full"> - <p className="text-sm font-medium">Queries performed:</p> - <div className="mt-1 flex w-full flex-col gap-2"> - <Skeleton className="h-2 w-full" /> - <Skeleton className="h-2 w-full" /> - <Skeleton className="h-2 w-full" /> - <Skeleton className="h-2 w-3/4" /> - </div> - </div> - - <div className="mt-6 flex w-full flex-col gap-6"> + <div className="flex w-full flex-col gap-6"> <Skeleton className="h-48 w-full" /> <Skeleton className="h-48 w-full" /> <Skeleton className="h-48 w-full" /> <Skeleton className="h-48 w-full" /> <Skeleton className="h-48 w-full" /> </div> </div> ); };apps/web/src/components/chat/message-status.tsx (1)
57-93: Remove commented-out legacy code.This dead code adds noise and should be removed. If the old implementation is needed for reference, it's preserved in version control history.
🔎 Proposed fix
); - // if (!status) - // return ( - // <ShinyText - // className="w-fit font-medium" - // shimmerWidth={40} - // disabled={!isLoading} - // > - // {isLoading ? "Generating answer..." : "Done!"} - // </ShinyText> - // ); - - // const queryString = queries - // ? queries.data.map((q, idx) => ( - // <i key={idx}> - // {q} - // {idx < queries.data.length - 1 && ", "} - // </i> - // )) - // : null; - - // // TODO: Searched for 1, 2, 3, +x other terms - // return ( - // <ShinyText - // className="w-fit font-medium" - // shimmerWidth={status.data === "searching" ? 40 : 100} - // disabled={!isLoading} - // > - // {isLoading - // ? { - // "generating-queries": "Generating queries...", - // searching: "Searching for ", - // "generating-answer": "Searched for ", - // }[status.data] - // : "Searched for "} - // {queryString} - // </ShinyText> - // ); };apps/web/src/lib/agentic/tools.ts (2)
46-46: Consider using DEFAULT_RERANKER constant instead of hardcoded value.The model
"zeroentropy:zerank-2"is hardcoded here butDEFAULT_RERANKERis already imported inbenchmark/route.tsfrom@agentset/validation. Using the constant ensures consistency.🔎 Proposed fix
+import { DEFAULT_RERANKER } from "@agentset/validation"; + // ...in semanticSearchTool execute: - rerank: rerank ?? { model: "zeroentropy:zerank-2", limit: 10 }, + rerank: rerank ?? { model: DEFAULT_RERANKER, limit: 10 },
34-38: Tool descriptions could be more informative for the LLM.The current descriptions ("Semantic search tool", "Keyword search tool") are minimal. More detailed descriptions help the LLM choose the right tool. Consider adding context about when to use each:
🔎 Suggested improvement
const semanticSearchTool = tool({ - description: "Semantic search tool", + description: "Search for information using semantic similarity. Best for conceptual or meaning-based queries.", inputSchema: z.object({ query: z.string().describe("The query to search for."), }), // ... }); const keywordSearchTool = supportsKeyword ? tool({ - description: "Keyword search tool", + description: "Search for information using exact keyword matching. Best for specific terms, names, or technical identifiers.", inputSchema: z.object({ query: z.string().describe("The query to search for."), }), // ... })Also applies to: 57-61
apps/web/src/lib/agentic/index.ts (2)
80-89: Minor: Remove unnecessary nullish coalescing.The
?? []on line 89 is redundant sinceflatMapalways returns an array, never null or undefined.🔎 Proposed fix
const searchResults = response.steps.flatMap((step) => step.toolResults .filter( (p) => p?.toolName === "semantic_search" || p?.toolName === "keyword_search", ) .flatMap((p) => p?.output), - ) ?? []; + );
91-94: Consider clarifying naming: "totalQueries" includes all tool calls.The variable
totalToolCallscorrectly counts all tool invocations (includingexpand), but it's passed toafterQueries, which semantically suggests query count. The current behavior charges usage for all tool calls, which may be intentional for billing purposes, but the naming could be clearer.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.tsapps/web/src/app/api/(internal-api)/benchmark/prompts.tsapps/web/src/app/api/(internal-api)/benchmark/route.tsapps/web/src/app/api/(internal-api)/benchmark/utils.tsapps/web/src/app/api/(internal-api)/chat/route.tsapps/web/src/app/api/(internal-api)/hosting-chat/route.tsapps/web/src/app/api/(internal-api)/hosting-search/route.tsapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/message-actions/index.tsxapps/web/src/components/chat/message-actions/logs.tsxapps/web/src/components/chat/message-status.tsxapps/web/src/components/chat/remark-citations.tsapps/web/src/lib/agentic/index.tsapps/web/src/lib/agentic/prompts.tsapps/web/src/lib/agentic/search.tsapps/web/src/lib/agentic/tools.tsapps/web/src/lib/agentic/utils.tsapps/web/src/lib/prompts.tsapps/web/src/types/ai.tspackages/validation/src/llm/constants.ts
💤 Files with no reviewable changes (5)
- apps/web/src/lib/agentic/prompts.ts
- apps/web/src/lib/agentic/utils.ts
- apps/web/src/app/api/(internal-api)/benchmark/prompts.ts
- apps/web/src/components/chat/message-actions/logs.tsx
- apps/web/src/lib/agentic/search.ts
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.{ts,tsx}: Use the App Router directory structure in Next.js projects
Mark client components explicitly with 'use client' directive
Place static content and interfaces at file end
When usinguseRouter, import from@bprogress/next/appto show global progress bar
Use Zod for form validation in forms
Use react-hook-form for form handling as defined inapps/web/src/components/ui/form.tsx
Files:
apps/web/src/lib/prompts.tsapps/web/src/app/api/(internal-api)/benchmark/utils.tsapps/web/src/components/chat/message-status.tsxapps/web/src/components/chat/remark-citations.tsapps/web/src/types/ai.tsapps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/app/api/(internal-api)/hosting-chat/route.tsapps/web/src/app/api/(internal-api)/hosting-search/route.tsapps/web/src/lib/agentic/tools.tsapps/web/src/app/api/(internal-api)/benchmark/route.tsapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.tsapps/web/src/components/chat/message-actions/index.tsxapps/web/src/app/api/(internal-api)/chat/route.tsapps/web/src/lib/agentic/index.ts
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}
📄 CodeRabbit inference engine (.cursor/rules/shadcn.mdc)
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}: Import Shadcn UI components from the @agentset/ui alias (e.g.,import { Button, Card } from "@agentset/ui")
Use Shadcn UI components from the packages/ui/src/components/ui directory for UI elements
Style components using the 'new-york' style variant with 'neutral' base color and CSS variables for theming as configured in components.json
The Button component supports an isLoading prop to display a loading spinner
Files:
apps/web/src/lib/prompts.tsapps/web/src/app/api/(internal-api)/benchmark/utils.tsapps/web/src/components/chat/message-status.tsxapps/web/src/components/chat/remark-citations.tsapps/web/src/types/ai.tsapps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/app/api/(internal-api)/hosting-chat/route.tsapps/web/src/app/api/(internal-api)/hosting-search/route.tsapps/web/src/lib/agentic/tools.tsapps/web/src/app/api/(internal-api)/benchmark/route.tsapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.tsapps/web/src/components/chat/message-actions/index.tsxapps/web/src/app/api/(internal-api)/chat/route.tsapps/web/src/lib/agentic/index.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import from packages using configured aliases (e.g.,
@agentset/ui,@agentset/db/client) instead of relative paths to packages
Files:
apps/web/src/lib/prompts.tsapps/web/src/app/api/(internal-api)/benchmark/utils.tsapps/web/src/components/chat/message-status.tsxapps/web/src/components/chat/remark-citations.tsapps/web/src/types/ai.tsapps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/app/api/(internal-api)/hosting-chat/route.tsapps/web/src/app/api/(internal-api)/hosting-search/route.tsapps/web/src/lib/agentic/tools.tsapps/web/src/app/api/(internal-api)/benchmark/route.tsapps/web/src/components/chat/citation-modal.tsxpackages/validation/src/llm/constants.tsapps/web/src/components/chat/citation-button.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.tsapps/web/src/components/chat/message-actions/index.tsxapps/web/src/app/api/(internal-api)/chat/route.tsapps/web/src/lib/agentic/index.ts
apps/web/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.tsx: Wrap client components in Suspense with fallback
Minimize use of 'useEffect' and 'setState' hooks
Show loading states during form submission
Files:
apps/web/src/components/chat/message-status.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/message-actions/index.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-21T13:02:44.489Z
Learnt from: max-programming
Repo: agentset-ai/agentset PR: 73
File: apps/web/src/app/app.agentset.ai/(dashboard)/[slug]/[namespaceSlug]/hosting/components/hosting-layout.tsx:19-19
Timestamp: 2025-12-21T13:02:44.489Z
Learning: Tailwind CSS v4 supports the spacing(n) shorthand inside arbitrary values (e.g., h-[calc(...(--spacing(16))...)) to compile to calc(var(--spacing) * n). When reviewing TSX files that use Tailwind and arbitrary values, verify any usage of --spacing within calc expressions is valid per Tailwind v4 docs and that the resulting CSS is correctly applied. If you introduce or rely on such spacing syntax, ensure the syntax is supported in the specific Tailwind build configuration and that there are no typos or unsupported nested expressions.
Applied to files:
apps/web/src/components/chat/message-status.tsxapps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/message-actions/index.tsx
🧬 Code graph analysis (9)
apps/web/src/lib/prompts.ts (2)
apps/web/src/lib/prompt.ts (1)
prmpt(14-40)packages/engine/src/vector-store/query.ts (1)
QueryVectorStoreResult(59-61)
apps/web/src/components/chat/message-status.tsx (1)
packages/ui/src/components/ai-elements/chain-of-thought.tsx (4)
ChainOfThought(43-71)ChainOfThoughtHeader(79-112)ChainOfThoughtContent(189-208)ChainOfThoughtStep(121-161)
apps/web/src/types/ai.ts (2)
packages/engine/src/vector-store/query.ts (1)
QueryVectorStoreResult(59-61)apps/web/src/lib/agentic/tools.ts (1)
AgenticTools(132-132)
apps/web/src/app/api/(internal-api)/hosting-chat/route.ts (2)
apps/web/src/lib/agentic/index.ts (1)
streamAgenticResponse(14-49)apps/web/src/lib/prompts.ts (1)
DEFAULT_SYSTEM_PROMPT(5-18)
apps/web/src/app/api/(internal-api)/benchmark/route.ts (3)
apps/web/src/lib/agentic/index.ts (1)
generateAgenticResponse(51-100)apps/web/src/lib/prompts.ts (1)
DEFAULT_SYSTEM_PROMPT(5-18)packages/validation/src/re-ranker/constants.ts (1)
DEFAULT_RERANKER(23-23)
apps/web/src/components/chat/citation-modal.tsx (3)
packages/ui/src/lib/utils.ts (1)
cn(5-7)packages/utils/src/string.ts (1)
truncate(28-31)packages/ui/src/components/ui/dialog.tsx (1)
DialogTitle(145-145)
apps/web/src/components/chat/citation-button.tsx (1)
apps/web/src/components/chat/citation-modal.tsx (1)
CitationModal(55-107)
apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts (1)
packages/engine/src/vector-store/query.ts (1)
QueryVectorStoreResult(59-61)
apps/web/src/app/api/(internal-api)/chat/route.ts (1)
apps/web/src/lib/agentic/index.ts (1)
streamAgenticResponse(14-49)
🪛 Biome (2.1.2)
apps/web/src/components/chat/citation-button.tsx
[error] 18-18: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (23)
apps/web/src/components/chat/message-actions/index.tsx (1)
5-5: LGTM! Import cleanup aligns with the removal of hosting-related UI.The removal of
LogsIconand retention ofCopyIconandRefreshCcwIconis consistent with the PR's goal to remove legacy agentic implementation and hosting-specific UI.apps/web/src/lib/prompts.ts (2)
3-6: LGTM!The import and prompt update to reference "search tools" aligns with the new tool-based agentic architecture.
12-12: Citation format updated to string-based IDs.The new
ref:prefix format aligns with the changes inremark-citations.tsandcitation-button.tsx. This provides more precise source attribution using chunk IDs.apps/web/src/app/api/(internal-api)/benchmark/utils.ts (1)
2-2: LGTM!Import path correctly updated to the new location of
formatSourcesin@/lib/prompts.apps/web/src/app/api/(internal-api)/hosting-search/route.ts (2)
80-91: LGTM!The simplified vector store query with semantic mode is clean. The rerank configuration correctly handles optional model with a fallback limit of 15.
93-98: Usage increment simplified.The usage tracking now increments by 1 per request rather than tracking total queries. This aligns with the removal of the multi-query agentic flow.
apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts (1)
30-41: LGTM!The response type correctly updated to
QueryVectorStoreResult["results"]to match the simplified API response shape. The data flow from API → hook → page is consistent.apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx (1)
101-112: LGTM!The rendering logic correctly handles the new array-based data shape with proper empty state handling.
apps/web/src/types/ai.ts (1)
1-14: LGTM!The simplified
MyUIMessagetype correctly integratesAgenticToolsand defines the"agentset-sources"data key that aligns with the citation resolution logic inCitationButton.packages/validation/src/llm/constants.ts (1)
21-21: GPT-5.1 verified as valid and available in OpenAI's API.GPT-5.1 is available in OpenAI's API with configurable reasoning effort levels, and the model identifier is correctly formatted per the codebase's LLM type system. This is a type-safe change, though note it will affect any downstream systems or users relying on the previous default behavior.
apps/web/src/components/chat/message-status.tsx (2)
24-29: LGTM on the tool-based filtering logic.The filtering correctly identifies the three tool types from the new agentic workflow. The type predicates align with the tool names defined in
apps/web/src/lib/agentic/tools.ts.
40-51: The optional chaining onpart.input?.queryis appropriate defensive programming. All three filtered tool types (semantic_search, keyword_search, expand) definequeryas required in their Zod schemas, but the AI SDK'sToolInvocationParttype allows theinputfield to be optional. This pattern is consistent across the codebase—the citation-button component similarly uses optional chaining foroutput(a.output ?? []). TheMyUIMessagetype correctly represents the tool part structure throughInferUITools<ReturnType<typeof getAgenticTools>>, and the fallback label handling ensures proper UI behavior when input is missing.apps/web/src/components/chat/remark-citations.ts (2)
25-25: LGTM on the regex update.The new regex
/\[ref:([^\]]+)\]/gcorrectly matches the citation format specified inDEFAULT_SYSTEM_PROMPT(e.g.,[ref:x86jecs6fauhiwn4rua4nyjk#a0f271bb-7a61-4341-8757-77a393cc9c80]). The[^\]]+pattern ensures it captures any characters except].
5-17: Type consistency looks correct for string-based citations.The interface, regex parsing, and property assignments all consistently use
stringfor thecitationId. This aligns with the broader PR changes shifting from numeric to string-based citation identifiers.Also applies to: 45-55
apps/web/src/app/api/(internal-api)/hosting-chat/route.ts (1)
78-83: LGTM on the streaming response migration.The switch to
streamAgenticResponsewith fullmessagesarray andtoUIMessageStreamResponse()aligns with the new tool-based agentic architecture. The usage tracking viaafterQueriesis preserved correctly.Also applies to: 113-131
apps/web/src/app/api/(internal-api)/benchmark/route.ts (2)
73-93: LGTM on the agentic path migration.The simplified
generateAgenticResponsecall correctly uses the new API shape. The response mapping to{ answer, sources }aligns with the return type fromgenerateAgenticResponse.
99-102: Good addition of DEFAULT_RERANKER for the non-agentic path.Using a centralized constant for the reranker model ensures consistency across the codebase.
apps/web/src/app/api/(internal-api)/chat/route.ts (2)
148-168: LGTM on the agentic streaming migration.The conditional
rerank: body.rerank ? {...} : undefinedcorrectly handles the optional reranking, unlike the similar code inhosting-chat/route.ts. The full messages array is passed correctly, and the response streaming is properly handled.
2-2: Import updated correctly.The named import
streamAgenticResponsereplaces the previous default import, aligning with the new API surface.apps/web/src/components/chat/citation-modal.tsx (1)
66-78: LGTM on the simplified trigger rendering.The unified button rendering with truncated hosting citation or fallback "Source" label is clean and consistent with the simplified citation flow.
apps/web/src/lib/agentic/tools.ts (2)
11-17: LGTM on formatChunks helper.The helper correctly normalizes chunks with a default empty object for missing metadata.
86-123: Filter field naming is correct; semantic search with filters is intentionalThe expandTool correctly uses
sequence_number(snake_case) in filters to match the actual metadata field stored from PartitionBatch, while the input schema parametersequenceNumber(camelCase) is properly used as the comparison value, not as the field name. No naming mismatch exists—this is appropriate TypeScript convention where camelCase parameters map to snake_case database fields.The semantic search mode with documentId and sequence_number filters is a valid architectural choice for context expansion. Results are ranked by semantic relevance to the query within the same document, which aligns with the tool's purpose of finding related chunks near a specified sequence number.
apps/web/src/lib/agentic/index.ts (1)
34-47: LGTM on streaming response implementation.The streaming setup with
onStepFinishandonFinishcallbacks correctly tracks tool usage and invokesafterQueries. ThestopWhen: stepCountIs(20)provides a reasonable safeguard against runaway generation.
|
|
||
| result = { | ||
| answer: response.text, | ||
| sources: response.searchResults as QueryVectorStoreResult["results"], |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the benchmark route file
find . -name "route.ts" -path "*/api/(internal-api)/benchmark*" | head -5
# Find the agentic index file
find . -name "index.ts" -path "*agentic*" | head -5
# Find QueryVectorStoreResult type
rg "type QueryVectorStoreResult" --type=ts -B2 -A5Repository: agentset-ai/agentset
Length of output: 470
🏁 Script executed:
#!/bin/bash
# Read the benchmark route file around line 92
cat -n ./apps/web/src/app/api/(internal-api)/benchmark/route.ts | sed -n '85,100p'
# Read the agentic/index.ts to see generateAgenticResponse
head -100 ./apps/web/src/lib/agentic/index.ts
# Read queryVectorStore function to understand QueryVectorStoreResult
cat -n ./packages/engine/src/vector-store/query.ts | head -80Repository: agentset-ai/agentset
Length of output: 233
🏁 Script executed:
#!/bin/bash
# Find and read the benchmark route file around line 92
BENCHMARK_FILE=$(find . -path "*/api/(internal-api)/benchmark/route.ts" -type f)
if [ -n "$BENCHMARK_FILE" ]; then
cat -n "$BENCHMARK_FILE" | sed -n '85,100p'
fi
# Read the agentic/index.ts to see generateAgenticResponse
head -150 ./apps/web/src/lib/agentic/index.ts
# Read queryVectorStore function to understand QueryVectorStoreResult
cat -n ./packages/engine/src/vector-store/query.ts | head -100Repository: agentset-ai/agentset
Length of output: 4819
Add explicit return type to generateAgenticResponse or validate searchResults shape.
The cast as QueryVectorStoreResult["results"] assumes response.searchResults matches the expected type, but searchResults in generateAgenticResponse is constructed from tool output without explicit type annotations. Consider explicitly typing the return type of generateAgenticResponse to include searchResults: QueryVectorStoreResult["results"], or add runtime validation before the cast.
🤖 Prompt for AI Agents
In apps/web/src/app/api/(internal-api)/benchmark/route.ts around line 92, the
code unsafely casts response.searchResults to QueryVectorStoreResult["results"];
update generateAgenticResponse to explicitly declare its return type including
searchResults: QueryVectorStoreResult["results"] so the compiler guarantees
shape, or add a small runtime validation before the cast (e.g., check
Array.isArray and required item properties) and throw/log a descriptive error if
validation fails; ensure the function signature and any upstream callers are
updated to use the new typed return value.
| rerank: { | ||
| model: hosting.rerankConfig?.model, | ||
| limit: hosting.rerankConfig?.limit, | ||
| }, |
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.
Possible issue: rerank object with undefined properties may bypass default reranking.
When hosting.rerankConfig is null, this creates rerank: { model: undefined, limit: undefined }. In tools.ts, the semanticSearchTool uses rerank ?? { model: "zeroentropy:zerank-2", limit: 10 }, so an object with undefined values won't trigger the fallback defaults.
Consider passing undefined when reranking is not configured:
🔎 Proposed fix
- rerank: {
- model: hosting.rerankConfig?.model,
- limit: hosting.rerankConfig?.limit,
- },
+ rerank: hosting.rerankConfig
+ ? {
+ model: hosting.rerankConfig.model,
+ limit: hosting.rerankConfig.limit,
+ }
+ : undefined,📝 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.
| rerank: { | |
| model: hosting.rerankConfig?.model, | |
| limit: hosting.rerankConfig?.limit, | |
| }, | |
| rerank: hosting.rerankConfig | |
| ? { | |
| model: hosting.rerankConfig.model, | |
| limit: hosting.rerankConfig.limit, | |
| } | |
| : undefined, |
🤖 Prompt for AI Agents
In apps/web/src/app/api/(internal-api)/hosting-chat/route.ts around lines 119 to
122, constructing rerank as { model: hosting.rerankConfig?.model, limit:
hosting.rerankConfig?.limit } can produce an object with undefined fields that
prevents tools.ts from using its fallback; change the assignment to pass
undefined when rerankConfig is absent (e.g. set rerank to hosting.rerankConfig ?
{ model: hosting.rerankConfig.model, limit: hosting.rerankConfig.limit } :
undefined) so the semanticSearchTool can correctly apply its default rerank
settings.
| const citationId = props["data-citation"]; | ||
| if (!props.children || !citationId || !message) return null; | ||
|
|
||
| const idx = props["data-citation"] ? props["data-citation"] - 1 : undefined; | ||
| const source = useMemo(() => { | ||
| return ( | ||
| message.parts.find((a) => a.type === "data-agentset-sources")?.data | ||
| ?.results ?? | ||
| message.parts | ||
| .filter( | ||
| (a) => | ||
| a.type === "tool-semantic_search" || | ||
| a.type === "tool-keyword_search", | ||
| ) | ||
| .flatMap((a) => a.output ?? []) | ||
| ).find((a) => a.id === citationId); | ||
| }, [message.parts, citationId]); |
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.
Critical: useMemo called conditionally after early return.
The useMemo hook at line 18 is called after the conditional return at line 16, violating React's Rules of Hooks. Hooks must be called unconditionally and in the same order on every render.
🔎 Proposed fix
export const CitationButton = ({
message,
...props
}: {
children?: React.ReactNode;
message?: MyUIMessage;
"data-citation"?: string;
className?: string;
}) => {
const citationId = props["data-citation"];
- if (!props.children || !citationId || !message) return null;
- const source = useMemo(() => {
- return (
+ const source = useMemo(() => {
+ if (!citationId || !message) return undefined;
+ return (
message.parts.find((a) => a.type === "data-agentset-sources")?.data
?.results ??
message.parts
.filter(
(a) =>
a.type === "tool-semantic_search" ||
a.type === "tool-keyword_search",
)
.flatMap((a) => a.output ?? [])
).find((a) => a.id === citationId);
}, [message?.parts, citationId]);
+ if (!props.children || !citationId || !message) return null;
+
if (!source) return <span {...props}>{props.children}</span>;
return <CitationModal source={source} triggerProps={props} />;
};📝 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 citationId = props["data-citation"]; | |
| if (!props.children || !citationId || !message) return null; | |
| const idx = props["data-citation"] ? props["data-citation"] - 1 : undefined; | |
| const source = useMemo(() => { | |
| return ( | |
| message.parts.find((a) => a.type === "data-agentset-sources")?.data | |
| ?.results ?? | |
| message.parts | |
| .filter( | |
| (a) => | |
| a.type === "tool-semantic_search" || | |
| a.type === "tool-keyword_search", | |
| ) | |
| .flatMap((a) => a.output ?? []) | |
| ).find((a) => a.id === citationId); | |
| }, [message.parts, citationId]); | |
| export const CitationButton = ({ | |
| message, | |
| ...props | |
| }: { | |
| children?: React.ReactNode; | |
| message?: MyUIMessage; | |
| "data-citation"?: string; | |
| className?: string; | |
| }) => { | |
| const citationId = props["data-citation"]; | |
| const source = useMemo(() => { | |
| if (!citationId || !message) return undefined; | |
| return ( | |
| message.parts.find((a) => a.type === "data-agentset-sources")?.data | |
| ?.results ?? | |
| message.parts | |
| .filter( | |
| (a) => | |
| a.type === "tool-semantic_search" || | |
| a.type === "tool-keyword_search", | |
| ) | |
| .flatMap((a) => a.output ?? []) | |
| ).find((a) => a.id === citationId); | |
| }, [message?.parts, citationId]); | |
| if (!props.children || !citationId || !message) return null; | |
| if (!source) return <span {...props}>{props.children}</span>; | |
| return <CitationModal source={source} triggerProps={props} />; | |
| }; |
🧰 Tools
🪛 Biome (2.1.2)
[error] 18-18: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🤖 Prompt for AI Agents
In apps/web/src/components/chat/citation-button.tsx around lines 15 to 30, the
useMemo hook is currently called after an early return which violates the Rules
of Hooks; move the useMemo call so it executes unconditionally (before any
return) and guard its internal logic to handle missing props/message (e.g.,
return null from the memo if required data is absent) so that hooks run in the
same order every render; keep the dependency array [message.parts, citationId]
and keep the same lookup logic inside the memo, then perform the early return
after the memo when children, citationId, or message are absent or when source
is null.
Remove legacy agentic implementation and add new one via tool calling
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.