Skip to content

Conversation

@ahmedriad1
Copy link
Member

@ahmedriad1 ahmedriad1 commented Dec 23, 2025

Remove legacy agentic implementation and add new one via tool calling

Summary by CodeRabbit

  • New Features

    • Introduced tool-based agentic search and new source-formatting helper.
  • Bug Fixes

    • Switched citations to string-based references and improved citation UI resilience.
  • Refactor

    • Simplified search flow and streaming responses; flattened search result shapes.
    • Removed detailed message logs and condensed message-status UI.
    • Updated default language model version to a newer release.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
app-agentset-ai Ready Ready Preview, Comment Dec 23, 2025 2:15pm

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

Refactors 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

Cohort / File(s) Summary
Search Page UI
apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx, apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts
Switched from object-shaped multi-query payloads (chunks, queries) to a direct results array; removed "Queries performed" UI and adjusted rendering to use data/data.map.
Agentic Core & Tools
apps/web/src/lib/agentic/index.ts, apps/web/src/lib/agentic/tools.ts, apps/web/src/lib/agentic/prompts.ts
Removed prior pipeline and query prompts; added getAgenticTools, streamAgenticResponse, generateAgenticResponse; toolset includes semantic_search, conditional keyword_search, and expand.
Removed Agentic Utilities
apps/web/src/lib/agentic/search.ts, apps/web/src/lib/agentic/utils.ts
Deleted multi-round agenticSearch orchestration, query generation/evaluation helpers, and source/formatting utilities.
Routes: Chat / Hosting / Benchmark / Search
apps/web/src/app/api/(internal-api)/chat/route.ts, apps/web/src/app/api/(internal-api)/hosting-chat/route.ts, apps/web/src/app/api/(internal-api)/hosting-search/route.ts, apps/web/src/app/api/(internal-api)/benchmark/route.ts, apps/web/src/app/api/(internal-api)/benchmark/utils.ts, apps/web/src/app/api/(internal-api)/benchmark/prompts.ts
Replaced agenticPipeline with streamAgenticResponse; flattened query options (embeddingModel, vectorStore, topK, rerank); hosting-search simplified to direct vector-store query returning results; centralized DEFAULT_SYSTEM_PROMPT/DEFAULT_RERANKER usage; removed refine prompts.
Citation System
apps/web/src/components/chat/citation-button.tsx, apps/web/src/components/chat/citation-modal.tsx, apps/web/src/components/chat/remark-citations.ts
Switched citation format from numeric indices to string refs ([ref:...]); prop/type changes (data-citation → string, citationId); removed sourceIndex usage and adjusted modal/hook interfaces.
Message UI & Logs
apps/web/src/components/chat/message-actions/index.tsx, apps/web/src/components/chat/message-actions/logs.tsx, apps/web/src/components/chat/message-status.tsx, apps/web/src/components/chat/remark-citations.ts
Removed legacy MessageLogs component and hosting logs UI; simplified MessageStatus to render tool-based parts (semantic/keyword/expand) and use tool call IDs as keys.
Prompts, Formatting & Types
apps/web/src/lib/prompts.ts, apps/web/src/types/ai.ts
Moved/added formatSources to lib/prompts, updated DEFAULT_SYSTEM_PROMPT to use [ref:...]; simplified MyUIMessage type to use agentset-sources and AgenticTools generic, removed old metadata/parts types.
Constants
packages/validation/src/llm/constants.ts
Updated DEFAULT_LLM from "openai:gpt-4.1" to "openai:gpt-5.1".

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)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Add support for Turbopuffer #14 — Overlapping vector-store/agentic refactor (getNamespaceEmbeddingModel/getNamespaceVectorStore, new queryVectorStore API).
  • Fix chat bugs #24 — Related changes to agentic pipeline and MessageStatus rendering; likely to touch similar files.
  • Improve chat UI #21 — Modifies chat UI and logs components that were removed/changed here; potential conflicts.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improved agentic chat' accurately summarizes the main objective: refactoring the agentic implementation using a new tool-based approach.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/improved-agentic-chat

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa03dfb and 27ba07c.

📒 Files selected for processing (1)
  • apps/web/src/components/chat/remark-citations.ts
🧰 Additional context used
📓 Path-based instructions (3)
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 using useRouter, import from @bprogress/next/app to show global progress bar
Use Zod for form validation in forms
Use react-hook-form for form handling as defined in apps/web/src/components/ui/form.tsx

Files:

  • apps/web/src/components/chat/remark-citations.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/components/chat/remark-citations.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/components/chat/remark-citations.ts
🔇 Additional comments (6)
apps/web/src/components/chat/remark-citations.ts (6)

3-3: LGTM: Proper traversal control import.

The addition of SKIP enables correct traversal control when inserting new nodes, preventing the visitor from re-processing the newly created citation nodes.


5-17: LGTM: Interface updated correctly.

The interface changes from numeric citationNumber to string citationId are consistent with the new reference-based citation format.


38-40: LGTM: Essential regex state reset.

Resetting lastIndex before each use is critical because the regex has the g flag, which maintains state between executions. Without this reset, the regex could skip citations in subsequent text nodes.


48-48: LGTM: Citation extraction and node construction updated correctly.

The changes consistently use string-based citation IDs throughout the extraction and node construction logic. The non-null assertion on line 48 is safe because the regex pattern requires at least one character in the capture group.

Also applies to: 53-53, 58-58


74-82: LGTM: Efficient node replacement with proper traversal control.

The refactored logic correctly:

  • Constructs all replacement nodes in a single pass
  • Splices them into the parent in one operation
  • Returns [SKIP, index + newNodes.length] to prevent re-visiting the inserted nodes and continue traversal at the correct position

The type assertion is safe because CitationNode is properly augmented into the mdast types.


25-25: Pattern permissiveness is intentional and necessary.

The regex /\[ref:([^\]]+)\]/g correctly handles the citation ID format documented in apps/web/src/lib/prompts.ts, where citation IDs follow the structure chunk_id#uuid (e.g., [ref:x86jecs6fauhiwn4rua4nyjk#a0f271bb-7a61-4341-8757-77a393cc9c80]). The character class [^\]]+ is appropriately permissive to match the hash symbol and hyphens in UUIDs. A more restrictive pattern would fail to parse valid citations.

Likely an incorrect or invalid review comment.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 ignores message prop changes.

The custom comparison function only checks isLoading, which means the component won't re-render when the message prop 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 claims isHosting won't change, this pattern can cause subtle bugs and will trigger ESLint warnings with eslint-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 formatSources function 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: Unused keywordEnabled field being queried.

The namespace.keywordEnabled field 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 but DEFAULT_RERANKER is already imported in benchmark/route.ts from @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 since flatMap always 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 totalToolCalls correctly counts all tool invocations (including expand), but it's passed to afterQueries, 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

📥 Commits

Reviewing files that changed from the base of the PR and between eed2b53 and fa03dfb.

📒 Files selected for processing (22)
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts
  • apps/web/src/app/api/(internal-api)/benchmark/prompts.ts
  • apps/web/src/app/api/(internal-api)/benchmark/route.ts
  • apps/web/src/app/api/(internal-api)/benchmark/utils.ts
  • apps/web/src/app/api/(internal-api)/chat/route.ts
  • apps/web/src/app/api/(internal-api)/hosting-chat/route.ts
  • apps/web/src/app/api/(internal-api)/hosting-search/route.ts
  • apps/web/src/components/chat/citation-button.tsx
  • apps/web/src/components/chat/citation-modal.tsx
  • apps/web/src/components/chat/message-actions/index.tsx
  • apps/web/src/components/chat/message-actions/logs.tsx
  • apps/web/src/components/chat/message-status.tsx
  • apps/web/src/components/chat/remark-citations.ts
  • apps/web/src/lib/agentic/index.ts
  • apps/web/src/lib/agentic/prompts.ts
  • apps/web/src/lib/agentic/search.ts
  • apps/web/src/lib/agentic/tools.ts
  • apps/web/src/lib/agentic/utils.ts
  • apps/web/src/lib/prompts.ts
  • apps/web/src/types/ai.ts
  • packages/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 using useRouter, import from @bprogress/next/app to show global progress bar
Use Zod for form validation in forms
Use react-hook-form for form handling as defined in apps/web/src/components/ui/form.tsx

Files:

  • apps/web/src/lib/prompts.ts
  • apps/web/src/app/api/(internal-api)/benchmark/utils.ts
  • apps/web/src/components/chat/message-status.tsx
  • apps/web/src/components/chat/remark-citations.ts
  • apps/web/src/types/ai.ts
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/app/api/(internal-api)/hosting-chat/route.ts
  • apps/web/src/app/api/(internal-api)/hosting-search/route.ts
  • apps/web/src/lib/agentic/tools.ts
  • apps/web/src/app/api/(internal-api)/benchmark/route.ts
  • apps/web/src/components/chat/citation-modal.tsx
  • apps/web/src/components/chat/citation-button.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts
  • apps/web/src/components/chat/message-actions/index.tsx
  • apps/web/src/app/api/(internal-api)/chat/route.ts
  • apps/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.ts
  • apps/web/src/app/api/(internal-api)/benchmark/utils.ts
  • apps/web/src/components/chat/message-status.tsx
  • apps/web/src/components/chat/remark-citations.ts
  • apps/web/src/types/ai.ts
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/app/api/(internal-api)/hosting-chat/route.ts
  • apps/web/src/app/api/(internal-api)/hosting-search/route.ts
  • apps/web/src/lib/agentic/tools.ts
  • apps/web/src/app/api/(internal-api)/benchmark/route.ts
  • apps/web/src/components/chat/citation-modal.tsx
  • apps/web/src/components/chat/citation-button.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts
  • apps/web/src/components/chat/message-actions/index.tsx
  • apps/web/src/app/api/(internal-api)/chat/route.ts
  • apps/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.ts
  • apps/web/src/app/api/(internal-api)/benchmark/utils.ts
  • apps/web/src/components/chat/message-status.tsx
  • apps/web/src/components/chat/remark-citations.ts
  • apps/web/src/types/ai.ts
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/app/api/(internal-api)/hosting-chat/route.ts
  • apps/web/src/app/api/(internal-api)/hosting-search/route.ts
  • apps/web/src/lib/agentic/tools.ts
  • apps/web/src/app/api/(internal-api)/benchmark/route.ts
  • apps/web/src/components/chat/citation-modal.tsx
  • packages/validation/src/llm/constants.ts
  • apps/web/src/components/chat/citation-button.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/use-search.ts
  • apps/web/src/components/chat/message-actions/index.tsx
  • apps/web/src/app/api/(internal-api)/chat/route.ts
  • apps/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.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/components/chat/citation-modal.tsx
  • apps/web/src/components/chat/citation-button.tsx
  • apps/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.tsx
  • apps/web/src/app/[hostingId]/(defaultLayout)/search/page.tsx
  • apps/web/src/components/chat/citation-modal.tsx
  • apps/web/src/components/chat/citation-button.tsx
  • apps/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 LogsIcon and retention of CopyIcon and RefreshCcwIcon is 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 in remark-citations.ts and citation-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 formatSources in @/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 MyUIMessage type correctly integrates AgenticTools and defines the "agentset-sources" data key that aligns with the citation resolution logic in CitationButton.

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 on part.input?.query is appropriate defensive programming. All three filtered tool types (semantic_search, keyword_search, expand) define query as required in their Zod schemas, but the AI SDK's ToolInvocationPart type allows the input field to be optional. This pattern is consistent across the codebase—the citation-button component similarly uses optional chaining for output (a.output ?? []). The MyUIMessage type correctly represents the tool part structure through InferUITools<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:([^\]]+)\]/g correctly matches the citation format specified in DEFAULT_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 string for the citationId. 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 streamAgenticResponse with full messages array and toUIMessageStreamResponse() aligns with the new tool-based agentic architecture. The usage tracking via afterQueries is 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 generateAgenticResponse call correctly uses the new API shape. The response mapping to { answer, sources } aligns with the return type from generateAgenticResponse.


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 ? {...} : undefined correctly handles the optional reranking, unlike the similar code in hosting-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 streamAgenticResponse replaces 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 intentional

The expandTool correctly uses sequence_number (snake_case) in filters to match the actual metadata field stored from PartitionBatch, while the input schema parameter sequenceNumber (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 onStepFinish and onFinish callbacks correctly tracks tool usage and invokes afterQueries. The stopWhen: stepCountIs(20) provides a reasonable safeguard against runaway generation.


result = {
answer: response.text,
sources: response.searchResults as QueryVectorStoreResult["results"],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 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 -A5

Repository: 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 -80

Repository: 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 -100

Repository: 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.

Comment on lines +119 to 122
rerank: {
model: hosting.rerankConfig?.model,
limit: hosting.rerankConfig?.limit,
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines +15 to +30
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]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants