Skip to content

Conversation

@google-labs-jules
Copy link
Contributor

@google-labs-jules google-labs-jules bot commented Oct 13, 2025

User description

This feature introduces a search mode selector that allows users to guide the AI agent's tool usage. By selecting a persona like "Geospatial" or "Web Search," the agent will prioritize relevant tools, leading to more accurate and context-aware responses. The update also enhances the map interaction by displaying multiple locations as a list of "fly to" links.


PR created automatically by Jules for task 9273094999750174910


PR Type

Enhancement, Bug fix


Description

  • Adds search mode selector UI with three personas: Standard, Geospatial, and Web Search

  • Implements dynamic system prompts and tool filtering based on selected persona

  • Enhances geospatial tool to support multiple locations with clickable "fly to" links

  • Refactors map query handler to process both single and multiple location responses


Diagram Walkthrough

flowchart LR
  UI["Search Mode Selector UI"] -- "searchMode parameter" --> Agent["Researcher Agent"]
  Agent -- "persona-based prompt" --> Tools["Filtered Tool Set"]
  Tools -- "geospatial response" --> Handler["Map Query Handler"]
  Handler -- "multiple locations" --> Links["Location Links Component"]
  Handler -- "single location" --> Map["Map Update"]
Loading

File Walkthrough

Relevant files
Enhancement
chat-panel.tsx
Add search mode selector UI component                                       

components/chat-panel.tsx

  • Adds RadioGroup component for search mode selection (Standard,
    Geospatial, Web Search)
  • Implements visual mode selector with icons (Search, MapPin, Globe)
  • Passes selected search mode as hidden form input to submit action
  • Adds state management for searchMode with default value 'Standard'
+42/-3   
actions.tsx
Pass search mode to researcher agent                                         

app/actions.tsx

  • Extracts searchMode from form data with 'Standard' as default
  • Passes searchMode parameter to researcher agent function
+4/-1     
researcher.tsx
Implement agent personas with dynamic prompts and tool filtering

lib/agents/researcher.tsx

  • Implements persona-based system prompts for each search mode
  • Filters available tools based on selected persona (Geospatial uses
    only geospatialQueryTool, Web Search uses search/retrieve)
  • Removes default system prompt in favor of mode-specific prompts
  • Adds searchMode parameter to function signature
+44/-54 
map-query-handler.tsx
Support multiple locations in map query responses               

components/map/map-query-handler.tsx

  • Adds support for multiple locations in geospatial tool response
  • Renders LocationLinks component when multiple locations are returned
  • Maintains single location handling with automatic map fly-to
  • Simplifies error handling and removes unused code
+31/-54 
location-links.tsx
Add location links component for multi-location display   

components/map/location-links.tsx

  • Creates new component to display multiple locations as clickable
    buttons
  • Implements handleFlyTo function to update map position on click
  • Uses MapPin icon and Button component for consistent UI
+45/-0   

Summary by CodeRabbit

  • New Features
    • Added selectable search modes in the chat panel: Standard, Geospatial, and Web Search, with icon-based controls. The chosen mode is included when sending messages.
    • Geospatial results now support multiple locations, displayed as clickable links; selecting one updates the map view. Single-location responses continue to auto-focus the map.
    • Introduced a locations list UI for quick navigation to places within map results.

- Adds a new UI component in the chat panel to allow users to select a search mode (Standard, Geospatial, or Web Search).
- The selected mode is passed to the AI agent with each request.
- The researcher agent now adopts a "persona" based on the selected mode, which dynamically adjusts its system prompt and restricts its available tools to improve relevance and accuracy.
- When the geospatial tool returns multiple locations, they are now rendered as a list of clickable "fly to" links in the chat, allowing the user to navigate the map to each location.
@google-labs-jules
Copy link
Contributor Author

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!


For security, I will only act on instructions from the user who triggered this task.

@vercel
Copy link

vercel bot commented Oct 13, 2025

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

Project Deployment Preview Comments Updated (UTC)
qcx Ready Ready Preview Comment Oct 13, 2025 7:52am

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds a user-selectable searchMode to the chat UI and form submission, threads it through app/actions to the researcher agent, and updates the researcher to use mode-specific prompts and tool sets. Introduces multi-location handling for map queries via a new LocationLinks component and refactors map-query-handler to support single vs. multiple locations.

Changes

Cohort / File(s) Summary
Form action wiring
app/actions.tsx
Extracts searchMode from formData (default 'Standard') and passes it to researcher, altering the iterative response generation path.
Chat UI: search mode selection
components/chat-panel.tsx
Adds RadioGroup for searchMode ("Standard", "Geospatial", "Web Search"), manages state, submits hidden input, updates icons/labels, and adjusts file input ref via callback.
Map: multi-location UI
components/map/location-links.tsx
New LocationLinks component rendering buttons for multiple locations; clicking updates map targetPosition and mapFeature via useMapData.
Map: query handling refactor
components/map/map-query-handler.tsx
Supports multiple locations: renders LocationLinks when multiple; flies to single valid location and sets mapFeature/mapUrl; simplifies useEffect and removes legacy logic.
Agent: mode-aware prompting/tools
lib/agents/researcher.tsx
researcher(...) gains optional searchMode; composes systemPrompt per mode (standard/geospatial/web); narrows tools based on mode; updates nonexperimental_streamText call accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ChatPanel as Chat Panel
  participant Actions as app/actions.tsx
  participant Researcher as lib/agents/researcher.tsx
  participant Tools as Tools Registry

  User->>ChatPanel: Select searchMode + submit message
  ChatPanel->>Actions: POST form (message, files, searchMode)
  Actions->>Researcher: researcher({ ..., searchMode })
  Researcher->>Researcher: Build systemPrompt based on searchMode
  Researcher->>Tools: getTools()
  Tools-->>Researcher: allTools
  Researcher->>Researcher: Derive availableTools per searchMode
  Researcher->>Researcher: nonexperimental_streamText(systemPrompt, availableTools)
  Researcher-->>Actions: Streamed response (incl. tool calls/results)
  Actions-->>ChatPanel: UI stream updates
  ChatPanel-->>User: Render assistant output
Loading
sequenceDiagram
  autonumber
  participant ToolOut as Tool Output (mcp_response)
  participant MapHandler as components/map/map-query-handler.tsx
  participant LocationLinks as components/map/location-links.tsx
  participant MapState as useMapData()

  ToolOut-->>MapHandler: locations[] / location
  alt Multiple locations
    MapHandler->>LocationLinks: Render list (place_name)
    User->>LocationLinks: Click location
    LocationLinks->>MapState: set targetPosition [lon, lat], mapFeature.place_name
  else Single valid location
    MapHandler->>MapState: set targetPosition [lon, lat], mapFeature { place_name, mapUrl }
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • CharlieHelps

Poem

I twitch my ears at modes three-fold—
Standard burrows, maps unfold,
Webby trails where links are spun,
Pins that point to rising sun.
With gentle hops through tools I glide,
Picking paths the users guide.
Thump! New searches, neatly tied.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 “Implement Search Mode Selector and Agent Personas” succinctly captures the core feature introduced by this PR—adding a search mode selector UI and corresponding persona-based behavior—without extraneous details, making the primary change clear to any reviewer.
✨ 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 feature/search-mode-personas

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24da001 and 341cde4.

📒 Files selected for processing (5)
  • app/actions.tsx (2 hunks)
  • components/chat-panel.tsx (3 hunks)
  • components/map/location-links.tsx (1 hunks)
  • components/map/map-query-handler.tsx (1 hunks)
  • lib/agents/researcher.tsx (2 hunks)

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.

@ngoiyaeric ngoiyaeric marked this pull request as ready for review October 13, 2025 07:51
@charliecreates charliecreates bot requested a review from CharlieHelps October 13, 2025 07:51
@qodo-merge-pro
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-merge-pro
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Decouple persona logic from agent

Abstract the hardcoded persona logic, including system prompts and tool
filtering, from the researcher agent. Instead, define each persona in a separate
configuration object to improve modularity and scalability.

Examples:

lib/agents/researcher.tsx [32-71]
  let systemPrompt = `Current date and time: ${currentDate}. Match the language of your response to the user's language. Always aim to directly address the user's question. If using information from a tool (like web search), cite the source URL.`;

  const standardPrompt = `As a comprehensive AI assistant, you can search the web, retrieve information from URLs, and understand geospatial queries to assist the user and display information on a map. When tools are not needed, provide direct, helpful answers based on your knowledge.

  Tool Usage Guide:
  - For general web searches: Use the 'search' tool.
  - For retrieving content from specific URLs: Use the 'retrieve' tool.
  - For any questions involving locations, places, or directions: You MUST use the 'geospatialQueryTool'.`;

  const geospatialPrompt = `You are a specialized Geospatial AI assistant. Your primary function is to understand and respond to geospatial queries. You MUST prioritize using the 'geospatialQueryTool' for any questions involving locations, places, addresses, geographical features, businesses, points of interest, distances, or directions. Only use other tools if geospatial queries are not applicable.`;

 ... (clipped 30 lines)

Solution Walkthrough:

Before:

// lib/agents/researcher.tsx
export async function researcher(
  ...,
  searchMode?: string
) {
  // ...
  const standardPrompt = `As a comprehensive AI assistant...`;
  const geospatialPrompt = `You are a specialized Geospatial AI assistant...`;
  const webSearchPrompt = `You are a specialized Web Search AI assistant...`;

  switch (searchMode) {
    case 'Geospatial': systemPrompt = ...; break;
    case 'Web Search': systemPrompt = ...; break;
    default: systemPrompt = ...; break;
  }

  const allTools = getTools(...);
  let availableTools = allTools;
  if (searchMode === 'Geospatial') {
    availableTools = { geospatialQueryTool: allTools.geospatialQueryTool };
  } else if (searchMode === 'Web Search') {
    availableTools = { search: allTools.search, retrieve: allTools.retrieve };
  }
  // ... call streamText with availableTools
}

After:

// lib/agents/personas.ts (new file)
export const personas = {
  'Standard': {
    prompt: "As a comprehensive AI assistant...",
    tools: ['search', 'retrieve', 'geospatialQueryTool']
  },
  'Geospatial': {
    prompt: "You are a specialized Geospatial AI assistant...",
    tools: ['geospatialQueryTool']
  },
  'Web Search': {
    prompt: "You are a specialized Web Search AI assistant...",
    tools: ['search', 'retrieve']
  }
};

// lib/agents/researcher.tsx (refactored)
import { personas } from './personas';
export async function researcher(..., searchMode = 'Standard') {
  const persona = personas[searchMode];
  const systemPrompt = `${persona.prompt}\n...`;
  const allTools = getTools(...);
  const availableTools = Object.fromEntries(
    Object.entries(allTools).filter(([key]) => persona.tools.includes(key))
  );
  // ... call streamText with availableTools
}
Suggestion importance[1-10]: 8

__

Why: This is a strong architectural suggestion that correctly identifies tight coupling in the researcher agent and proposes a more modular, scalable design by abstracting persona logic into configurations.

Medium
General
Align agent prompts with available tools

Update the geospatialPrompt and webSearchPrompt to accurately reflect the
limited tools available in their respective modes, instructing the agent to
state its limitations for out-of-scope queries.

lib/agents/researcher.tsx [41-43]

-const geospatialPrompt = `You are a specialized Geospatial AI assistant. Your primary function is to understand and respond to geospatial queries. You MUST prioritize using the 'geospatialQueryTool' for any questions involving locations, places, addresses, geographical features, businesses, points of interest, distances, or directions. Only use other tools if geospatial queries are not applicable.`;
+const geospatialPrompt = `You are a specialized Geospatial AI assistant. Your primary function is to understand and respond to geospatial queries. You MUST use the 'geospatialQueryTool' for any questions involving locations, places, addresses, geographical features, businesses, points of interest, distances, or directions. If the user asks a non-geospatial question, state that you are a geospatial assistant and can only answer location-based queries.`;
 
-const webSearchPrompt = `You are a specialized Web Search AI assistant. Your primary function is to search the web and retrieve information from URLs to answer user questions. You MUST prioritize using the 'search' and 'retrieve' tools. Only use other tools if web searches are not applicable.`;
+const webSearchPrompt = `You are a specialized Web Search AI assistant. Your primary function is to search the web and retrieve information from URLs to answer user questions. You MUST use the 'search' and 'retrieve' tools for relevant queries. If the user asks a question that cannot be answered with a web search (e.g., a geospatial query), state that you are a web search assistant and cannot perform that type of task.`;
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a mismatch between the agent prompts and the available tools for specific search modes, and the proposed change improves prompt accuracy, preventing potential LLM errors.

Medium
Use stable keys and memoize handlers

In the LocationLinks component, replace the array index key with a stable,
unique identifier derived from location data. Additionally, memoize the
handleFlyTo function using useCallback to prevent unnecessary re-renders.

components/map/location-links.tsx [17-45]

+import { useCallback } from 'react';
+
 export const LocationLinks: React.FC<LocationLinksProps> = ({ locations }) => {
   const { setMapData } = useMapData();
 
-  const handleFlyTo = (location: Location) => {
+  const handleFlyTo = useCallback((location: Location) => {
     setMapData(prevData => ({
       ...prevData,
       targetPosition: [location.longitude, location.latitude],
       mapFeature: {
         place_name: location.place_name,
       }
     }));
-  };
+  }, [setMapData]);
 
   return (
     <div className="flex flex-col space-y-2">
-      {locations.map((location, index) => (
+      {locations.map((location) => (
         <Button
-          key={index}
+          key={`${location.place_name}-${location.latitude}-${location.longitude}`}
           variant="outline"
           className="justify-start"
           onClick={() => handleFlyTo(location)}
         >
           <MapPin className="mr-2 h-4 w-4" />
           {location.place_name}
         </Button>
       ))}
     </div>
   );
 };

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out the use of an array index as a React key and proposes a more stable, unique key, which is a best practice. The addition of useCallback is a valid, though minor, performance optimization.

Low
Simplify ref attachment on file input

Simplify the file input's ref by directly assigning fileInputRef instead of
using a more verbose callback function.

components/chat-panel.tsx [215-223]

 <input
   type="file"
-  ref={fileInputNode => {
-    fileInputRef.current = fileInputNode
-  }}
+  ref={fileInputRef}
   onChange={handleFileChange}
   className="hidden"
   accept="text/plain,image/png,image/jpeg,image/webp"
 />
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies that the PR introduced a more complex callback ref pattern where a simpler, direct ref object would suffice, improving code readability and adhering to common React idioms.

Low
  • More

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

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

  • The chat panel uses invalid id values with spaces, breaking label–input associations; fix to space-free ids. Also make the RadioGroup controlled and add accessible labels.
  • dynamicSystemPrompt is ignored in the researcher, likely a functional regression; integrate it with the persona prompt. Also avoid any for tools and consider not over-restricting tool availability.
  • Map handler no longer clears stale map state when no valid location is present; add a fallback reset. Duplicated Location types should be centralized.
  • Use stable React keys in LocationLinks instead of array indexes.
Additional notes (2)
  • Maintainability | components/chat-panel.tsx:174-178
    The RadioGroup is semi-controlled: you set local state via onValueChange, but rely on defaultValue rather than binding value. This can lead to subtle UI state issues during re-renders or hydration. Prefer a fully controlled component by passing value={searchMode} and removing defaultValue.

  • Maintainability | components/map/map-query-handler.tsx:7-11
    Location is defined in both map-query-handler.tsx and location-links.tsx. Duplicated types tend to drift over time. Centralizing the type improves maintainability.

Summary of changes
  • Added search mode selection UI in components/chat-panel.tsx using a RadioGroup with Standard, Geospatial, and Web Search options, and posted it via a hidden searchMode input.
  • Captured searchMode in app/actions.tsx from FormData and forwarded it to the researcher agent.
  • Updated lib/agents/researcher.tsx to introduce personas (Standard/Geospatial/Web Search), adjust the system prompt accordingly, and restrict available tools per persona.
  • Enhanced map behavior in components/map/map-query-handler.tsx to handle multiple locations; when multiple are returned, renders LocationLinks to let users "fly to" individual locations.
  • Introduced components/map/location-links.tsx to render clickable links for multiple locations.
  • Minor refactor to file input ref handling in the chat panel.

Comment on lines +197 to +205
<RadioGroupItem value="Web Search" id="Web Search" className="sr-only" />
<label
htmlFor="Web Search"
className={`cursor-pointer rounded-full px-3 py-1 text-sm ${
searchMode === 'Web Search' ? 'bg-background text-foreground' : 'text-muted-foreground'
}`}
>
<Globe className="h-4 w-4" />
</label>

Choose a reason for hiding this comment

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

id attributes must not contain spaces. Using id="Web Search" can break the label–input association and CSS selectors, leading to the labels not toggling the radio inputs reliably. This is a correctness bug in the new UI.

Suggestion

Use space-free ids and keep the display values as-is. Also bind value to state for consistency (see next comment). For example:

<RadioGroup
  value={searchMode}
  onValueChange={setSearchMode}
  defaultValue="Standard"
  className="flex bg-muted rounded-full p-1"
>
  <RadioGroupItem value="Standard" id="standard" className="sr-only" />
  <label htmlFor="standard" className={`cursor-pointer rounded-full px-3 py-1 text-sm ${searchMode === 'Standard' ? 'bg-background text-foreground' : 'text-muted-foreground'}`}>
    <Search className="h-4 w-4" />
  </label>

  <RadioGroupItem value="Geospatial" id="geospatial" className="sr-only" />
  <label htmlFor="geospatial" className={`cursor-pointer rounded-full px-3 py-1 text-sm ${searchMode === 'Geospatial' ? 'bg-background text-foreground' : 'text-muted-foreground'}`}>
    <MapPin className="h-4 w-4" />
  </label>

  <RadioGroupItem value="Web Search" id="web-search" className="sr-only" />
  <label htmlFor="web-search" className={`cursor-pointer rounded-full px-3 py-1 text-sm ${searchMode === 'Web Search' ? 'bg-background text-foreground' : 'text-muted-foreground'}`}>
    <Globe className="h-4 w-4" />
  </label>
</RadioGroup>

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +180 to +205
<label
htmlFor="Standard"
className={`cursor-pointer rounded-full px-3 py-1 text-sm ${
searchMode === 'Standard' ? 'bg-background text-foreground' : 'text-muted-foreground'
}`}
>
<Search className="h-4 w-4" />
</label>
<RadioGroupItem value="Geospatial" id="Geospatial" className="sr-only" />
<label
htmlFor="Geospatial"
className={`cursor-pointer rounded-full px-3 py-1 text-sm ${
searchMode === 'Geospatial' ? 'bg-background text-foreground' : 'text-muted-foreground'
}`}
>
<MapPin className="h-4 w-4" />
</label>
<RadioGroupItem value="Web Search" id="Web Search" className="sr-only" />
<label
htmlFor="Web Search"
className={`cursor-pointer rounded-full px-3 py-1 text-sm ${
searchMode === 'Web Search' ? 'bg-background text-foreground' : 'text-muted-foreground'
}`}
>
<Globe className="h-4 w-4" />
</label>

Choose a reason for hiding this comment

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

The icon-only labels lack accessible names. Screen-reader users won’t know what each control does. Provide accessible labels (e.g., aria-label) or include visually hidden text.

Suggestion

Add aria-label to each label or include an sr-only span:

<label htmlFor="standard" aria-label="Standard mode" className={...}>
  <Search className="h-4 w-4" />
</label>
<label htmlFor="geospatial" aria-label="Geospatial mode" className={...}>
  <MapPin className="h-4 w-4" />
</label>
<label htmlFor="web-search" aria-label="Web Search mode" className={...}>
  <Globe className="h-4 w-4" />
</label>

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +32 to +55
let systemPrompt = `Current date and time: ${currentDate}. Match the language of your response to the user's language. Always aim to directly address the user's question. If using information from a tool (like web search), cite the source URL.`;

const standardPrompt = `As a comprehensive AI assistant, you can search the web, retrieve information from URLs, and understand geospatial queries to assist the user and display information on a map. When tools are not needed, provide direct, helpful answers based on your knowledge.
Tool Usage Guide:
- For general web searches: Use the 'search' tool.
- For retrieving content from specific URLs: Use the 'retrieve' tool.
- For any questions involving locations, places, or directions: You MUST use the 'geospatialQueryTool'.`;

const geospatialPrompt = `You are a specialized Geospatial AI assistant. Your primary function is to understand and respond to geospatial queries. You MUST prioritize using the 'geospatialQueryTool' for any questions involving locations, places, addresses, geographical features, businesses, points of interest, distances, or directions. Only use other tools if geospatial queries are not applicable.`;

const webSearchPrompt = `You are a specialized Web Search AI assistant. Your primary function is to search the web and retrieve information from URLs to answer user questions. You MUST prioritize using the 'search' and 'retrieve' tools. Only use other tools if web searches are not applicable.`;

switch (searchMode) {
case 'Geospatial':
systemPrompt = `${geospatialPrompt}\n${systemPrompt}`;
break;
case 'Web Search':
systemPrompt = `${webSearchPrompt}\n${systemPrompt}`;
break;
default:
systemPrompt = `${standardPrompt}\n${systemPrompt}`;
break;
}

Choose a reason for hiding this comment

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

dynamicSystemPrompt is now ignored. This is a functional regression from the previous implementation where a caller-provided system prompt could shape the agent’s behavior. The new persona logic should augment or allow override by dynamicSystemPrompt, not discard it.

Suggestion

Combine the persona prompt with dynamicSystemPrompt, giving the dynamic prompt precedence if present:

let systemPrompt = `Current date and time: ${currentDate}. Match the language of your response to the user's language. Always aim to directly address the user's question. If using information from a tool (like web search), cite the source URL.`;

// Build personaPrompt based on searchMode as you do now...

// If a dynamic system prompt is provided, prepend or override
if (dynamicSystemPrompt && dynamicSystemPrompt.trim() !== '') {
  systemPrompt = `${dynamicSystemPrompt.trim()}\n\n${systemPrompt}`;
}

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +57 to +65
const allTools = getTools({ uiStream, fullResponse });
let availableTools: any = allTools;

if (searchMode === 'Geospatial') {
availableTools = { geospatialQueryTool: allTools.geospatialQueryTool };
} else if (searchMode === 'Web Search') {
availableTools = { search: allTools.search, retrieve: allTools.retrieve };
}

Choose a reason for hiding this comment

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

Avoid any for availableTools. It weakens type safety and risks passing malformed tool definitions to the model at runtime. You can keep the correct shape with a Partial<typeof allTools> and narrow explicitly.

Suggestion

Preserve types by using Partial<typeof allTools> and explicit construction:

const allTools = getTools({ uiStream, fullResponse });
let availableTools: Partial<typeof allTools> = allTools;

if (searchMode === 'Geospatial') {
  availableTools = { geospatialQueryTool: allTools.geospatialQueryTool };
} else if (searchMode === 'Web Search') {
  availableTools = { search: allTools.search, retrieve: allTools.retrieve };
}

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +60 to +64
if (searchMode === 'Geospatial') {
availableTools = { geospatialQueryTool: allTools.geospatialQueryTool };
} else if (searchMode === 'Web Search') {
availableTools = { search: allTools.search, retrieve: allTools.retrieve };
}

Choose a reason for hiding this comment

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

Over-restricting tools by removing them entirely can degrade fallback behavior. For example, the Geospatial persona might still benefit from retrieve to cite sources. Consider keeping complementary tools available and relying on the persona prompt to prioritize usage, rather than hard removal.

Suggestion

Keep complementary tools available for personas while instructing prioritization via the prompt. For example, allow retrieve with Geospatial:

if (searchMode === 'Geospatial') {
  availableTools = {
    geospatialQueryTool: allTools.geospatialQueryTool,
    retrieve: allTools.retrieve
  };
}

Alternatively, leave all tools available and rely solely on the persona prompt to guide selection.

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +34 to 51
if (toolOutput && toolOutput.mcp_response) {
const { locations, location } = toolOutput.mcp_response;

if (typeof latitude === 'number' && typeof longitude === 'number') {
console.log(`MapQueryHandler: Received data from geospatialTool. Place: ${place_name}, Lat: ${latitude}, Lng: ${longitude}`);
setMapData(prevData => ({
...prevData,
// Ensure coordinates are in [lng, lat] format for MapboxGL
targetPosition: [longitude, latitude],
// Optionally store more info from mcp_response if needed by MapboxMap component later
mapFeature: {
place_name,
// Potentially add mapUrl or other details from toolOutput.mcp_response
mapUrl: toolOutput.mcp_response?.mapUrl
}
}));
if (locations && locations.length > 1) {
// Multiple locations: handled by LocationLinks, no automatic fly-to
} else {
console.warn("MapQueryHandler: Invalid latitude/longitude in toolOutput.mcp_response:", toolOutput.mcp_response.location);
// Clear target position if data is invalid
setMapData(prevData => ({
...prevData,
targetPosition: null,
mapFeature: null
}));
const singleLocation = locations && locations.length === 1 ? locations[0] : location;
if (singleLocation && typeof singleLocation.latitude === 'number' && typeof singleLocation.longitude === 'number') {
setMapData(prevData => ({
...prevData,
targetPosition: [singleLocation.longitude, singleLocation.latitude],
mapFeature: {
place_name: singleLocation.place_name,
mapUrl: toolOutput.mcp_response?.mapUrl
}
}));
}
}

Choose a reason for hiding this comment

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

When the tool output is invalid (no valid coordinates), the previous implementation cleared targetPosition/mapFeature. The new version silently does nothing, which can leave stale map state visible. Consider resetting the map state when no valid single location is present and there aren’t multiple locations to show.

Suggestion

Add a fallback reset when no valid single location is available and there aren’t multiple locations:

if (locations && locations.length > 1) {
  // handled by LocationLinks
} else {
  const singleLocation = locations && locations.length === 1 ? locations[0] : location;
  if (singleLocation && typeof singleLocation.latitude === 'number' && typeof singleLocation.longitude === 'number') {
    setMapData(prevData => ({
      ...prevData,
      targetPosition: [singleLocation.longitude, singleLocation.latitude],
      mapFeature: {
        place_name: singleLocation.place_name,
        mapUrl: toolOutput.mcp_response?.mapUrl
      }
    }));
  } else {
    setMapData(prevData => ({ ...prevData, targetPosition: null, mapFeature: null }));
  }
}

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +33 to +41
<Button
key={index}
variant="outline"
className="justify-start"
onClick={() => handleFlyTo(location)}
>
<MapPin className="mr-2 h-4 w-4" />
{location.place_name}
</Button>

Choose a reason for hiding this comment

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

Using the array index as a React key can cause unnecessary re-renders or state mismatches if the list changes. Even if the list is static per response, a stable key is preferable.

Suggestion

Use a stable key derived from the location itself, e.g., coordinates or place_name:

{locations.map((location) => (
  <Button
    key={`${location.longitude},${location.latitude}`}
    variant="outline"
    className="justify-start"
    onClick={() => handleFlyTo(location)}
  >
    <MapPin className="mr-2 h-4 w-4" />
    {location.place_name}
  </Button>
))}

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines +136 to +137
const searchMode = formData?.get('searchMode') as string || 'Standard'

Choose a reason for hiding this comment

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

You’re trusting the client-provided searchMode string without validation. This allows unexpected values to flow into your agent logic and could lead to inconsistent prompts/tool availability. Validate and normalize it to a known set of modes before use.

Suggestion

Validate the value before using it:

const rawSearchMode = (formData?.get('searchMode') as string) ?? 'Standard'
const allowedModes = new Set(['Standard', 'Geospatial', 'Web Search'])
const searchMode = allowedModes.has(rawSearchMode) ? rawSearchMode : 'Standard'

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

@charliecreates charliecreates bot removed the request for review from CharlieHelps October 13, 2025 07:57
@ngoiyaeric
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants