From bdb9cb4382cf9a6daf51b02ffda7542815f992e3 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 5 Dec 2025 15:09:04 -0500 Subject: [PATCH 1/7] initial grounding prototype --- .../src/components/Layout/RightSidebar.tsx | 119 ++++++++++++++++-- .../src/components/Specific/ChatMessage.tsx | 48 +++++-- .../src/services/firebaseAIService.ts | 4 + ai/ai-react-app/src/views/ChatView.tsx | 7 ++ 4 files changed, 156 insertions(+), 22 deletions(-) diff --git a/ai/ai-react-app/src/components/Layout/RightSidebar.tsx b/ai/ai-react-app/src/components/Layout/RightSidebar.tsx index 9e703ee35..5c79c850f 100644 --- a/ai/ai-react-app/src/components/Layout/RightSidebar.tsx +++ b/ai/ai-react-app/src/components/Layout/RightSidebar.tsx @@ -6,6 +6,7 @@ import { AVAILABLE_IMAGEN_MODELS, defaultFunctionCallingTool, defaultGoogleSearchTool, + defaultGoogleMapsTool, } from "../../services/firebaseAIService"; import { ModelParams, @@ -160,17 +161,49 @@ const RightSidebar: React.FC = ({ nextState.toolConfig = undefined; // Clear config when turning off } } else if (name === "google-search-toggle") { + let currentTools = nextState.tools ? [...nextState.tools] : []; if (checked) { - // Turn ON Google Search Grounding - nextState.tools = [defaultGoogleSearchTool]; - + // Add Google Search if not present + if (!currentTools.some((t) => "googleSearch" in t)) { + currentTools.push(defaultGoogleSearchTool); + } // Turn OFF JSON mode and Function Calling nextState.generationConfig.responseMimeType = undefined; nextState.generationConfig.responseSchema = undefined; nextState.toolConfig = undefined; } else { - // Turn OFF Google Search Grounding - nextState.tools = undefined; + // Remove Google Search + currentTools = currentTools.filter((t) => !("googleSearch" in t)); + } + nextState.tools = currentTools.length > 0 ? currentTools : undefined; + } else if (name === "google-maps-toggle") { + let currentTools = nextState.tools ? [...nextState.tools] : []; + if (checked) { + // Add Google Maps if not present + if (!currentTools.some((t) => "googleMaps" in t)) { + currentTools.push(defaultGoogleMapsTool); + } + // Turn OFF JSON mode and Function Calling + nextState.generationConfig.responseMimeType = undefined; + nextState.generationConfig.responseSchema = undefined; + nextState.toolConfig = undefined; + } else { + // Remove Google Maps + currentTools = currentTools.filter((t) => !("googleMaps" in t)); + } + nextState.tools = currentTools.length > 0 ? currentTools : undefined; + } else if (name === "google-maps-widget-toggle") { + let currentTools = nextState.tools ? [...nextState.tools] : []; + const mapToolIndex = currentTools.findIndex((t) => "googleMaps" in t); + if (mapToolIndex !== -1) { + // Update existing tool + currentTools[mapToolIndex] = { + googleMaps: { + enableWidget: checked, + }, + }; + console.error("DEDB tool: ", currentTools[mapToolIndex]); + nextState.tools = currentTools; } } console.log("[RightSidebar] Updated generative params state:", nextState); @@ -236,6 +269,13 @@ const RightSidebar: React.FC = ({ const isGroundingWithGoogleSearchActive = !!generativeParams.tools?.some( (tool) => "googleSearch" in tool, ); + const isGroundingWithGoogleMapsActive = !!generativeParams.tools?.some( + (tool) => "googleMaps" in tool, + ); + // Check if widget is enabled in the first Google Maps tool found + const isGoogleMapsWidgetEnabled = !!generativeParams.tools?.find( + (tool) => "googleMaps" in tool, + )?.googleMaps?.enableWidget; return (
@@ -378,11 +418,13 @@ const RightSidebar: React.FC = ({ checked={isStructuredOutputActive} onChange={handleToggleChange} disabled={ - isFunctionCallingActive || isGroundingWithGoogleSearchActive + isFunctionCallingActive || + isGroundingWithGoogleSearchActive || + isGroundingWithGoogleMapsActive } />
@@ -399,7 +441,8 @@ const RightSidebar: React.FC = ({ onChange={handleToggleChange} disabled={ isStructuredOutputActive || - isGroundingWithGoogleSearchActive + isGroundingWithGoogleSearchActive || + isGroundingWithGoogleMapsActive } /> = ({ name="google-search-toggle" checked={isGroundingWithGoogleSearchActive} onChange={handleToggleChange} - disabled={isStructuredOutputActive || isFunctionCallingActive} + disabled={ + isStructuredOutputActive || isFunctionCallingActive + } /> + + +
+ +
+ {/* Indented Widget Toggle */} +
+ + +
)} diff --git a/ai/ai-react-app/src/components/Specific/ChatMessage.tsx b/ai/ai-react-app/src/components/Specific/ChatMessage.tsx index 6c10f4fa4..f3eadc6fc 100644 --- a/ai/ai-react-app/src/components/Specific/ChatMessage.tsx +++ b/ai/ai-react-app/src/components/Specific/ChatMessage.tsx @@ -37,6 +37,23 @@ const getMessageText = (message: Content): string => { .join(""); }; +const getChunkDisplayData = ( + chunk: GroundingChunk, +): { title: string; uri?: string } => { + if (chunk.web) { + return { + title: chunk.web.title || chunk.web.uri || "Unknown Web Source", + uri: chunk.web.uri, + }; + } else if (chunk.maps) { + return { + title: chunk.maps.title || chunk.maps.text || "Unknown Maps Source", + uri: chunk.maps.uri, + }; + } + return { title: "Unknown Source" }; +}; + const renderTextWithInlineHighlighting = ( text: string, supports: GroundingSupport[], @@ -89,7 +106,7 @@ const renderTextWithInlineHighlighting = ( const tooltipText = seg.chunkIndices .map((ci) => { const chunk = chunks[ci - 1]; // ci is 1-based - return chunk.web?.title || chunk.web?.uri || `Source ${ci}`; + return getChunkDisplayData(chunk).title; }) .join("; "); @@ -190,23 +207,30 @@ const ChatMessage: React.FC = ({ groundingMetadata.groundingChunks.length > 0 && ( <>
Sources:
- )} diff --git a/ai/ai-react-app/src/services/firebaseAIService.ts b/ai/ai-react-app/src/services/firebaseAIService.ts index 36c4f4911..edca1a1fc 100644 --- a/ai/ai-react-app/src/services/firebaseAIService.ts +++ b/ai/ai-react-app/src/services/firebaseAIService.ts @@ -72,6 +72,10 @@ export const defaultGoogleSearchTool: GoogleSearchTool = { googleSearch: {} } +export const defaultGoogleMapsTool = { + googleMaps: {} +} + export const defaultGenerativeParams: Omit = { // Model name itself is selected in the UI generationConfig: { diff --git a/ai/ai-react-app/src/views/ChatView.tsx b/ai/ai-react-app/src/views/ChatView.tsx index f2dd7dd17..edce72129 100644 --- a/ai/ai-react-app/src/views/ChatView.tsx +++ b/ai/ai-react-app/src/views/ChatView.tsx @@ -188,6 +188,13 @@ const ChatView: React.FC = ({ console.log( `[ChatView] Grounding Metadata: ${finalModelCandidate?.groundingMetadata}`, ); + + if (finalModelCandidate?.groundingMetadata) { + console.log("DEDB lastGroundingMetadata: ", JSON.stringify(finalModelCandidate?.groundingMetadata)); + } else { + console.log("DEDB no grounding metadata"); + } + setLastGroundingMetadata( finalModelCandidate?.groundingMetadata || null, ); From 74f97efff8f2d4bced8be8dcfcf4cd7469d48384 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 5 Dec 2025 15:38:04 -0500 Subject: [PATCH 2/7] Better visualizations, lat/long protections --- .../src/components/Common/PromptInput.tsx | 6 +- .../components/Layout/RightSidebar.module.css | 20 ++ .../src/components/Layout/RightSidebar.tsx | 311 +++++++++++------- ai/ai-react-app/src/views/ChatView.tsx | 12 + 4 files changed, 225 insertions(+), 124 deletions(-) diff --git a/ai/ai-react-app/src/components/Common/PromptInput.tsx b/ai/ai-react-app/src/components/Common/PromptInput.tsx index 93bfc9d6a..7add61221 100644 --- a/ai/ai-react-app/src/components/Common/PromptInput.tsx +++ b/ai/ai-react-app/src/components/Common/PromptInput.tsx @@ -25,6 +25,7 @@ interface PromptInputProps { currentParams?: ModelParams; currentImagenParams?: ImagenModelParams; selectedFile: File | null; + disabled?: boolean; } const PromptInput: React.FC = ({ @@ -39,6 +40,7 @@ const PromptInput: React.FC = ({ aiInstance, currentParams, selectedFile, + disabled = false, }) => { const [tokenCount, setTokenCount] = useState( null, @@ -142,14 +144,14 @@ const PromptInput: React.FC = ({ value={prompt} onChange={(e) => onPromptChange(e.target.value)} placeholder={placeholder} - disabled={isLoading || isCountingTokens} // Disable if main loading OR counting + disabled={disabled || isLoading || isCountingTokens} // Disable if main loading OR counting rows={3} aria-label="Prompt input" />