feat(ai): extend FluxAI navigation with cross-page routing
The navigate_to_section tool now supports two modes:
A) Same-page scroll — scrollIntoView to real homepage DOM IDs
(technology, applications-dashboard, applications-deep, global,
our-story, legacy)
B) Cross-page routing — router.push to /applications/{slug},
/news, /heritage, /parts with automatic locale prefix.
Fixed: system prompt listed phantom section IDs (hero, news,
heritage, timeline, parts-catalog, contact) that don't exist in
the DOM — causing all non-homepage navigations to silently fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -114,7 +114,7 @@ TOOL USAGE RULES:
|
||||
1. SEARCH INSTALLATIONS: Use 'search_installations' to find real installations from our database. This is a DATA tool — you receive the results and reason about them before responding.
|
||||
2. SHOW CASE STUDY: Use 'show_case_study' to display a rich case study card for a specific installation. Requires a nodeId (get it from search_installations first) or an application slug for auto-match.
|
||||
3. SAVINGS/ROI: Use 'energy_savings_calculator' when discussing costs, energy, ROI. If volume is missing, assume 500 kg/h and 16h/day.
|
||||
4. NAVIGATION: Use 'navigate_to_section' to move the user around the site.
|
||||
4. NAVIGATION: Use 'navigate_to_section' to move the user around the site. Two modes: (A) "section" for homepage scroll — valid IDs: "technology", "applications-dashboard", "applications-deep", "global", "our-story", "legacy". (B) "url" for cross-page navigation — e.g. url="/applications/textile-drying", url="/news", url="/heritage", url="/parts". ALWAYS use mode B for news, heritage, parts, and application detail pages.
|
||||
5. COMPARISONS: Use 'process_comparison_table' for RF vs conventional tech.
|
||||
6. EXPLAIN TECH: Use 'rf_technology_explainer' for physics/mechanism questions.
|
||||
7. APPLICATION KNOWLEDGE: Use 'get_application_knowledge' to retrieve deep technical theory, advantages, and datasheets from our knowledge base.
|
||||
@@ -459,13 +459,39 @@ export async function POST(req: Request) {
|
||||
},
|
||||
}),
|
||||
|
||||
// ── TOOL 6: Navigate to Section (client-side — NO execute) ──
|
||||
// ── TOOL 6: Navigate (client-side — NO execute) ──────────────
|
||||
// Handles BOTH same-page scrolling (section) and cross-page
|
||||
// routing (url). The client inspects which field is set.
|
||||
navigate_to_section: tool({
|
||||
description: `Maps the user to a specific section of the FLUX website. Use when the user says "show me", "take me to", "where is", or asks about a specific page section. Available sections: "hero", "applications-dashboard", "applications-deep", "global" (globe), "timeline", "heritage", "news", "parts-catalog", "contact".`,
|
||||
description: `Navigate the user to any part of the FLUX website.
|
||||
|
||||
TWO MODES:
|
||||
A) SAME-PAGE SCROLL — set "section" to scroll to a homepage element by its DOM id:
|
||||
"technology" (hero/intro), "applications-dashboard", "applications-deep", "global" (interactive globe), "our-story" (timeline), "legacy" (Patrizio legacy)
|
||||
ONLY use these exact IDs. They only work when the user is on the homepage.
|
||||
|
||||
B) CROSS-PAGE NAVIGATION — set "url" to a route path (WITHOUT locale prefix). The client adds the locale automatically:
|
||||
"/news" — news hub listing
|
||||
"/news/{slug}" — specific article (use a real slug from context)
|
||||
"/heritage" — company heritage deep-dive
|
||||
"/parts" — spare parts catalog (B2B portal)
|
||||
"/applications/{slug}" — application detail page (use real slug from the database list above)
|
||||
|
||||
RULES:
|
||||
- ALWAYS prefer mode B for news, heritage, parts, and application detail pages.
|
||||
- Only use mode A for scrolling within the homepage.
|
||||
- When using mode B, use application slugs from the database list in this prompt.
|
||||
- "show me textile drying" → url="/applications/textile-drying"
|
||||
- "take me to the news" → url="/news"
|
||||
- "show me the heritage" → url="/heritage"
|
||||
- "show me the spare parts" → url="/parts"
|
||||
- "show me the globe" → section="global"
|
||||
- "go to the top" → section="technology"`,
|
||||
inputSchema: z.object({
|
||||
section: z.string().describe('Target section ID'),
|
||||
section: z.string().optional().describe('Homepage element ID for same-page scroll'),
|
||||
url: z.string().optional().describe('Route path for cross-page navigation (e.g. "/applications/textile-drying", "/news", "/heritage"). No locale prefix.'),
|
||||
subAction: z.enum(['none', 'activate-tab', 'highlight-node']).default('none'),
|
||||
tabId: z.string().optional().describe('Application slug to activate'),
|
||||
tabId: z.string().optional().describe('Application slug to activate on dashboard tab'),
|
||||
nodeId: z.string().optional().describe('Globe node ID to highlight'),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Sparkles, ArrowRight, X, Minus, Database, Maximize2, Minimize2 } from "
|
||||
import { useChat } from "@ai-sdk/react";
|
||||
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from "ai";
|
||||
import { useUIStore } from "@/lib/store/uiStore";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { useState, useEffect, useRef, useMemo } from "react";
|
||||
|
||||
// ── Renderers ──
|
||||
@@ -24,6 +25,10 @@ export default function SilentObserver() {
|
||||
setHighlightedMapNode, setSelectedMarkerId,
|
||||
} = useUIStore();
|
||||
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const locale = pathname?.split('/')[1] || 'en';
|
||||
|
||||
const [input, setInput] = useState("");
|
||||
const [isDark, setIsDark] = useState(false);
|
||||
const [isWideMode, setIsWideMode] = useState(false);
|
||||
@@ -68,24 +73,38 @@ export default function SilentObserver() {
|
||||
if (toolCall.dynamic) return;
|
||||
|
||||
if (toolCall.toolName === "navigate_to_section") {
|
||||
const { section, subAction, tabId, nodeId } = toolCall.input as {
|
||||
section: string; subAction?: string; tabId?: string; nodeId?: string;
|
||||
const { section, url, subAction, tabId, nodeId } = toolCall.input as {
|
||||
section?: string; url?: string; subAction?: string; tabId?: string; nodeId?: string;
|
||||
};
|
||||
handleClose();
|
||||
setTimeout(() => {
|
||||
const el = document.getElementById(section);
|
||||
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
if (subAction === "activate-tab" && tabId) setActiveApplicationTab(tabId);
|
||||
if (subAction === "highlight-node" && nodeId) {
|
||||
setHighlightedMapNode(nodeId);
|
||||
setTimeout(() => setHighlightedMapNode(null), 5000);
|
||||
}
|
||||
}, 400);
|
||||
addToolOutput({
|
||||
tool: "navigate_to_section" as any,
|
||||
toolCallId: toolCall.toolCallId,
|
||||
output: `Navigated to "${section}" section${tabId ? `, activated tab "${tabId}"` : ""}${nodeId ? `, highlighted node "${nodeId}"` : ""}`,
|
||||
});
|
||||
|
||||
if (url) {
|
||||
// Mode B: Cross-page navigation
|
||||
setTimeout(() => {
|
||||
router.push(`/${locale}${url}`);
|
||||
}, 400);
|
||||
addToolOutput({
|
||||
tool: "navigate_to_section" as any,
|
||||
toolCallId: toolCall.toolCallId,
|
||||
output: `Navigated to page "${url}"`,
|
||||
});
|
||||
} else if (section) {
|
||||
// Mode A: Same-page scroll (existing behavior)
|
||||
setTimeout(() => {
|
||||
const el = document.getElementById(section);
|
||||
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
if (subAction === "activate-tab" && tabId) setActiveApplicationTab(tabId);
|
||||
if (subAction === "highlight-node" && nodeId) {
|
||||
setHighlightedMapNode(nodeId);
|
||||
setTimeout(() => setHighlightedMapNode(null), 5000);
|
||||
}
|
||||
}, 400);
|
||||
addToolOutput({
|
||||
tool: "navigate_to_section" as any,
|
||||
toolCallId: toolCall.toolCallId,
|
||||
output: `Navigated to "${section}" section${tabId ? `, activated tab "${tabId}"` : ""}${nodeId ? `, highlighted node "${nodeId}"` : ""}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user