Sasquatch: local fast-path for finder questions (kills ~30s AI wait)

Root cause of the ~35s "spots in bangkok" response: every live-data question
first waited on an AI round-trip just to produce a tool call, and the keyless
backup model (Pollinations) can take tens of seconds. The relay query and local
formatting were already fast — the AI was the bottleneck.

Fix: before touching the AI, try parseLocalIntent(). For clear finder questions
(spots/events/coaches/crews/shops/tours/gear + community stats + weather with a
parseable location) we now go STRAIGHT to the relays (capped at 4.5s) and render
locally — zero AI calls. Expected ~35s → ~2–4s on the most common queries.

Deliberately still routed through the AI:
- trip planning (needs the rich itinerary card)
- personal "my …" Pro queries (get_my_* tools)
- anything without a confidently-parseable intent/location

If the fast-path relay query errors, it falls through to the existing AI loop as
a backup, so nothing regresses.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-09 23:24:08 +07:00
parent 8b7d9ca4ea
commit 6df526bf27

View file

@ -864,6 +864,68 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
return;
}
// 1d. LOCAL FAST-PATH — the big speed win.
// For clear finder questions ("spots in bangkok", "events in lisbon",
// "coaches near berlin", "community stats", "weather in X") we can parse
// the intent + location ourselves and go STRAIGHT to the relays — skipping
// the AI entirely. Previously every live query waited on an AI round-trip
// just to produce a tool call, which is what made these take ~30s+.
//
// We deliberately DON'T fast-path:
// • trip planning (needs the rich itinerary card + AI nuance)
// • personal "my …" queries (Pro tools handled by the AI path)
// Those fall through to the AI loop below.
const isPersonalQuery = /\bmy (stash|gear|crew|crews|spots?|listings?|inventory)\b|what am i selling|what do i own/i.test(question);
const fastIntent = isPersonalQuery ? null : parseLocalIntent(question);
if (fastIntent && fastIntent.tool !== 'plan_trip') {
try {
setStreamingText('🔍 Checking live data…');
let fastResult: unknown;
switch (fastIntent.tool) {
case 'find_spots':
fastResult = await cvm.findSpots({ city: fastIntent.location, limit: 30 });
break;
case 'find_events':
fastResult = await cvm.findEvents({ city: fastIntent.location, limit: 30 });
break;
case 'find_coaches':
fastResult = await cvm.findCoaches({ location: fastIntent.location, limit: 30 });
break;
case 'find_crews':
fastResult = await cvm.findCrews({ city: fastIntent.location, limit: 30 });
break;
case 'find_shops':
fastResult = await cvm.findShops({ city: fastIntent.location, limit: 30 });
break;
case 'find_tours':
fastResult = await cvm.findTours({ city: fastIntent.location, limit: 30 });
break;
case 'find_gear':
fastResult = await cvm.findGear({ search: fastIntent.search, limit: 30 });
break;
case 'get_community_stats':
fastResult = await cvm.getCommunityStats();
break;
case 'get_weather':
fastResult = { content: [{ type: 'text', text: await fetchWeatherForCity(fastIntent.city) }] };
break;
}
setCvmConnected(true);
setStreamingText('');
appendMessage({
role: 'sasquatch',
text: formatLocalResults(fastIntent, fastResult),
timestamp: Date.now(),
isLive: true,
});
return;
} catch (fastErr) {
// Relays hiccuped — fall through to the AI path as a backup.
console.warn('[Sasquatch] fast-path failed, falling back to AI:', fastErr);
setStreamingText('');
}
}
// 2. Build conversation history for AI
const history = chatMessages.map((m) => ({
role: m.role === 'user' ? ('user' as const) : ('assistant' as const),