diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index 88df299..2ec29a8 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -89,43 +89,56 @@ Example of a perfect autonomous flow: 6. You output your final text referencing real data, the case study card, and gently offer a consultation. ═══════════════════════════════════════════ -SALES METHODOLOGY — SPIN FRAMEWORK: +SALES METHODOLOGY — FUNNEL-AWARE SPIN: ═══════════════════════════════════════════ -Before deploying tools, qualify the prospect through natural conversation: -S (Situación): What's their current process? What method? What volume? -P (Problema): What's not working? Energy costs? Quality issues? Speed? -I (Implicación): What does the problem cost them? Rejected batches? Downtime? -N (Necesidad): Confirm the need before recommending. +STAGE 1 — QUALIFY (S+P from SPIN): +Trigger: User mentions an industry or problem WITHOUT specifics. +Action: Ask 1-2 qualifying questions. DO NOT fire tools yet. + Example: "Estoy en textiles" → "What specific process — post-dye drying, finishing, moisture leveling? And what method do you use currently?" + Example: "I need to reduce costs" → "Which industry and production process? What throughput per hour?" + +STAGE 2 — RECOMMEND + EDUCATE: +Trigger: User provides industry + process OR industry + problem. +Action: Call 'recommend_application' first to match their needs to FLUX products. Then chain 'rf_technology_explainer' or 'get_application_knowledge' for the top match. + Example: User says "I dry textiles after dyeing, about 800 kg/h" → recommend_application → navigate to the recommended app page → get_application_knowledge. + +STAGE 3 — QUANTIFY + PROVE: +Trigger: User understands the application and wants numbers. +Action: Chain 'energy_savings_calculator' → 'search_installations' → 'show_case_study' with the most relevant real installation. + +STAGE 4 — SPECIFY + CONVERT: +Trigger: User asks about equipment, pricing, or next steps. +Action: 'show_equipment_specs' → 'schedule_consultation'. This is the PRIMARY goal. RULES: -- If the user mentions an industry WITHOUT specifics → ask 1-2 qualifying questions BEFORE firing tools. - Example: "Estoy en textiles" → "What specific process are you evaluating — post-dye drying, finishing, moisture leveling? And what method do you currently use?" -- If the user provides DETAILED context (industry + process + volume OR problem) → proceed directly to tools. -- Never fire more than 2 tools in a single autonomous sequence without including meaningful analysis text. -- EXCEPTION: If the user explicitly asks for something specific ("show me case studies", "calculate savings for 500 kg/h"), deliver it immediately. - -IDEAL CONVERSION FLOW: -Qualify → Educate (explainer/comparison) → Quantify (calculator) → Prove (case study) → Recommend (equipment specs) → Convert (consultation) +- Progress through stages naturally. Do not skip Stage 1 unless the user provides enough context. +- If the user provides DETAILED context (industry + process + volume + problem), jump to Stage 2 or 3. +- EXCEPTION: If the user explicitly asks for something specific ("show me case studies", "calculate savings for 500 kg/h"), deliver it immediately regardless of stage. +- Never fire more than 3 tools in a single autonomous sequence without including analysis text between them. +- After EVERY tool result, include a brief human-readable insight before the next tool or suggestion. ═══════════════════════════════════════════ 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. 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. -8. EQUIPMENT SPECS: Use 'show_equipment_specs' to display real machine specifications from an actual installation. -9. CONSULTATION (PRIMARY GOAL): Use 'schedule_consultation' when the user shows buying intent. +1. RECOMMEND APPLICATION (NEW — Stage 2): Use 'recommend_application' when the user describes their industry or problem and you need to identify which FLUX product fits. This is your FIRST tool when entering Stage 2. It returns ranked matches from the database. +2. SEARCH INSTALLATIONS: Use 'search_installations' to find real installations. DATA tool — read results and reference them in your response. +3. SHOW CASE STUDY: Use 'show_case_study' for a rich case study card. Get nodeId from search_installations first. +4. SAVINGS/ROI: Use 'energy_savings_calculator' for cost/energy/ROI discussions. Default: 500 kg/h, 16h/day if volume unknown. +5. NAVIGATION: Use 'navigate_to_section'. Mode A "section" for homepage scroll (valid: "technology", "applications-dashboard", "applications-deep", "global", "our-story", "legacy"). Mode B "url" for cross-page (url="/applications/{slug}", url="/news", url="/heritage", url="/parts"). +6. COMPARISONS: Use 'process_comparison_table' for RF vs conventional tech. +7. EXPLAIN TECH: Use 'rf_technology_explainer' for physics/mechanism questions. +8. APPLICATION KNOWLEDGE: Use 'get_application_knowledge' for deep technical theory after identifying the right application. +9. EQUIPMENT SPECS: Use 'show_equipment_specs' for real machine specifications. +10. CONSULTATION (PRIMARY GOAL): Use 'schedule_consultation' when the user shows buying intent. -PROACTIVE NEXT STEPS: -After showing results, gently suggest the logical next action: - savings → case study ("We have real installations proving these numbers...") - case study → equipment specs ("Want to see the technical specs of the system used?") - equipment → consultation ("Shall I arrange a conversation with our engineering team?") +PROACTIVE NEXT STEPS (always suggest the next logical action): + recommend → "Let me show you the details of this application..." → navigate to app page or get_application_knowledge + knowledge/explainer → "Want to see what this means for your energy costs?" → energy_savings_calculator + savings → "We have real installations proving these numbers..." → search_installations + show_case_study + case study → "Want to see the technical specs of the system used?" → show_equipment_specs + equipment → "Shall I arrange a conversation with our engineering team?" → schedule_consultation + comparison → "Let me quantify the difference for your specific operation..." → energy_savings_calculator LANGUAGE: Respond in the exact same language the user writes in.`; } @@ -199,6 +212,106 @@ export async function POST(req: Request) { // DATA TOOLS (have execute, return data for AI to reason about) // ══════════════════════════════════════════════════════════════ + // ── TOOL 0: Recommend Application (DATA — smart product matching) ── + recommend_application: tool({ + description: `Analyze the user's needs and recommend the best FLUX application(s) from the database. Use this FIRST when a prospect describes their industry, problem, or process without specifying a particular FLUX product. Returns ranked matches with confidence scores and reasoning. After getting results, chain into 'navigate_to_section' (url) to show them the application page, or 'get_application_knowledge' for deep technical detail.`, + inputSchema: z.object({ + industryKeywords: z.array(z.string()) + .describe('Keywords about their industry, e.g. ["textile", "fabric", "drying", "moisture"]'), + problemDescription: z.string() + .describe('What the user is trying to solve, e.g. "too much moisture after dyeing, high energy costs"'), + processType: z.string().optional() + .describe('Specific process if mentioned, e.g. "post-dye drying", "defrosting meat blocks"'), + currentMethod: z.string().optional() + .describe('Their current equipment/method if mentioned, e.g. "stenter", "steam autoclave"'), + }), + execute: async ({ industryKeywords, problemDescription, processType, currentMethod }) => { + const apps = await prisma.application.findMany({ + where: { isActive: true }, + select: { + slug: true, + title: true, + subtitle: true, + category: true, + shortDescription: true, + heroDescription: true, + dashboardMetricsJson: true, + }, + }); + + // Score each application against the user's needs + const scored = apps.map((app: any) => { + const searchText = `${app.title} ${app.subtitle || ''} ${app.shortDescription} ${app.category} ${(app.heroDescription || '').slice(0, 800)}`.toLowerCase(); + let score = 0; + const matchedKeywords: string[] = []; + + for (const kw of industryKeywords) { + if (searchText.includes(kw.toLowerCase())) { + score += 10; + matchedKeywords.push(kw); + } + } + + // Check problem description words + const problemWords = problemDescription.toLowerCase().split(/\s+/).filter((w: string) => w.length > 3); + for (const pw of problemWords) { + if (searchText.includes(pw)) score += 3; + } + + // Bonus for process type match + if (processType && searchText.includes(processType.toLowerCase())) score += 15; + + // Bonus for current method match (they're looking to replace it) + if (currentMethod && searchText.includes(currentMethod.toLowerCase())) score += 8; + + return { + slug: app.slug, + title: app.title, + subtitle: app.subtitle, + category: app.category, + shortDescription: app.shortDescription, + score, + matchedKeywords, + metrics: safeParseJson(app.dashboardMetricsJson, []), + }; + }); + + const ranked = scored + .filter((a: any) => a.score > 0) + .sort((a: any, b: any) => b.score - a.score) + .slice(0, 3); + + if (ranked.length === 0) { + return { + found: 0, + message: 'No direct match found. Ask the user for more details about their specific process and materials.', + allApplications: apps.map((a: any) => ({ slug: a.slug, title: a.title, category: a.category })), + }; + } + + return { + found: ranked.length, + recommendations: ranked.map((r: any, idx: number) => ({ + rank: idx + 1, + slug: r.slug, + title: r.title, + subtitle: r.subtitle, + category: r.category, + shortDescription: r.shortDescription, + matchedKeywords: r.matchedKeywords, + confidenceScore: Math.min(100, r.score), + topMetrics: r.metrics.slice(0, 3), + })), + userContext: { + industryKeywords, + problemDescription, + processType: processType || null, + currentMethod: currentMethod || null, + }, + }; + }, + }), + // ── TOOL 1: Search Installations (DATA — queries Prisma) ── search_installations: tool({ description: `Search the FLUX global installation database for real case studies and projects. Returns summaries for you to analyze and reference in your response. Use when you need to find relevant installations to back up your claims, or when the user asks about references, case studies, or proven results. You can then call 'show_case_study' with a specific nodeId to display the full card to the user.`, diff --git a/src/components/ai/SilentObserver.tsx b/src/components/ai/SilentObserver.tsx index 848066b..c41e71f 100644 --- a/src/components/ai/SilentObserver.tsx +++ b/src/components/ai/SilentObserver.tsx @@ -187,6 +187,11 @@ export default function SilentObserver() { const DataToolWorking = ({ label }: { label: string }) => (
{label}
); const ToolError = ({ msg }: { msg?: string }) => (
Analysis unavailable. {msg}
); + if (part.type === "tool-recommend_application") { + if (part.state === "input-streaming" || part.state === "input-available") return ; + if (part.state === "output-available") return null; + if (part.state === "output-error") return ; + } if (part.type === "tool-search_installations") { if (part.state === "input-streaming" || part.state === "input-available") return ; if (part.state === "output-available") return null; @@ -237,6 +242,41 @@ export default function SilentObserver() { return null; } + // ═══ Contextual Quick-Replies based on last assistant message ═══ + function getContextualSuggestions(): string[] { + if (isLoading || messages.length === 0) return []; + const lastAssistant = [...messages].reverse().find(m => m.role === "assistant"); + if (!lastAssistant?.parts) return []; + + const toolTypes = new Set( + lastAssistant.parts + .filter((p: any) => p.type?.startsWith("tool-") && p.state === "output-available") + .map((p: any) => p.type) + ); + + // Priority order: suggest the next logical funnel step + if (toolTypes.has("tool-schedule_consultation")) return []; // End of funnel + if (toolTypes.has("tool-show_equipment_specs")) + return ["Schedule a consultation", "Compare with traditional methods"]; + if (toolTypes.has("tool-show_case_study")) + return ["Show me equipment specs", "Calculate savings for my operation", "Schedule a consultation"]; + if (toolTypes.has("tool-energy_savings_calculator")) + return ["Show me a real installation", "See equipment specs", "How does RF heating work?"]; + if (toolTypes.has("tool-process_comparison_table")) + return ["Calculate savings for my operation", "Show me proven installations"]; + if (toolTypes.has("tool-rf_technology_explainer")) + return ["What would I save in energy costs?", "Show me real installations"]; + if (toolTypes.has("tool-recommend_application") || toolTypes.has("tool-get_application_knowledge")) + return ["Calculate energy savings", "Show me case studies", "Compare RF vs my current method"]; + if (toolTypes.has("tool-navigate_to_section")) + return ["Tell me more about this", "How much energy can I save?"]; + + // Default: if assistant responded with text only (Stage 1 qualification) + return []; + } + + const suggestions = getContextualSuggestions(); + return ( <> @@ -323,6 +363,24 @@ export default function SilentObserver() { {[0, 1, 2].map((i) => ())} )} + {suggestions.length > 0 && !isLoading && ( + + {suggestions.map((s) => ( + + ))} + + )}