Make Sasquatch tell the truth: direct relay queries, not a CVM server
The chat's data layer (useCVMClient) queries Nostr relays directly from the browser — the CVM/ContextVM server round-trip was removed earlier for speed. But the UI and system prompt still claimed data came "from the CVM server", which is no longer true. Aligned every user-facing claim with reality: - System prompt: reframed "CVM Tools / MUST route through CVM" as "Live data tools that query the Nostr relays directly". Tool-calling behaviour unchanged. - Streaming status: "Querying CVM for X" → "Searching the relays for X". - Tool-failure message: no longer blames a non-existent "CVM server"; now correctly frames it as relays being slow, with a retry nudge. - Empty results: stop asserting "not added yet" as fact — an empty result can also mean slow relays or a misspelled place. Now offers retry + add-a-spot, and names the place searched. Applied to the local formatter, the system prompt rules, and the second-pass AI instruction. - Chat footer: "Powered by ContextVM & Nostr Relays Data" → "Live data from Nostr relays" (links the primary community relay). - About page credit: "AI powered by ContextVM" → "Trust & crew chat via ContextVM" (its real role: Relatr trust scores + encrypted crew chat). - Owner CVM health panel: clarified it probes the standalone MCP server and that the in-app chat does not depend on it. - Updated stale internal comments in SasquatchGuide + CreateSpotPage. ContextVM references that ARE accurate (Relatr trust scores, crew-chat Cordn coordinator) were left intact. Build clean, no console errors. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
ee23f3e063
commit
5506344893
4 changed files with 37 additions and 29 deletions
|
|
@ -80,7 +80,9 @@ export function CVMHealthCheck() {
|
|||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
Health probe for the Sasquatch CVM data server
|
||||
Heartbeat probe for the standalone ContextVM MCP server. Note: the
|
||||
in-app Sasquatch chat reads Nostr relays directly and does not depend
|
||||
on this server.
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* SasquatchGuide — AI-powered skate assistant
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* Uses OpenRouter free model (no credits needed, works for any user) for
|
||||
* natural language understanding, then calls the SkateSpots CVM server on
|
||||
* Nostr for live data (spots, events, coaches, gear).
|
||||
* natural language understanding, then queries the Nostr relays directly
|
||||
* (useCVMClient) for live data (spots, events, coaches, gear).
|
||||
* Falls back to static FAQ if AI is unavailable.
|
||||
*/
|
||||
|
||||
|
|
@ -126,15 +126,15 @@ interface ChatMessage {
|
|||
}
|
||||
|
||||
// ─── System prompt for the AI ─────────────────────────────────────────────────
|
||||
// Sasquatch is the user-facing agent. The CVM server is the data layer —
|
||||
// it aggregates and crunches Nostr events across multiple relays, deduplicates,
|
||||
// filters by features inside events (things Nostr filters alone can't do),
|
||||
// and returns structured results. Sasquatch MUST always route data queries
|
||||
// through CVM — never guess from training data.
|
||||
// Sasquatch is the user-facing agent. The data tools below query Nostr relays
|
||||
// directly from the browser (see useCVMClient) — they aggregate events across
|
||||
// the community relays, deduplicate, and run feature filters that plain Nostr
|
||||
// filters can't. Sasquatch MUST always answer data questions by calling a tool —
|
||||
// never guess spots/events/people from training data.
|
||||
|
||||
const SYSTEM_PROMPT = `You are The Sasquatch, the AI guide for SkateSpots — a decentralized skateboarding platform built on Nostr. Chill, skater-friendly, concise.
|
||||
|
||||
## CVM Tools — query live data
|
||||
## Live data tools — query the Nostr relays
|
||||
Respond with ONLY a JSON tool call when you need data. Nothing else on that line.
|
||||
{"tool":"<name>","args":{}}
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ Args: city?(string), country?(string), difficulty?("beginner"|"intermediate"|"ad
|
|||
- Filter checks: city tag, location text, spot title, description — all case-insensitive
|
||||
- For country queries use city of the capital/main city too: Thailand → use location:"Bangkok"
|
||||
- Always use limit:20+ for city/country searches
|
||||
- 0 results = not added yet, encourage user to add via skate-spots.org/map
|
||||
- 0 results could mean none listed yet OR the relays were slow — be honest, suggest a retry and adding via skate-spots.org/map (don't state "nobody added one" as a fact)
|
||||
Example: {"tool":"find_spots","args":{"city":"Bangkok","limit":20}}
|
||||
|
||||
### find_events
|
||||
|
|
@ -200,7 +200,7 @@ Note: this returns a rich itinerary CARD — after calling it, add a short one-l
|
|||
3. App/protocol/how-to questions → answer directly (no tool). Wallet, Supporters, and the private Stash have NO live search tool — answer from the App facts below and point users to the right page. Spots, Events, the Lab (all crafts incl. judges), Gear, Crews, Shops and Tours all HAVE live tools — use them for "find/show" requests.
|
||||
4. Be concise, skater-friendly
|
||||
5. Never invent spot names, event dates, or user data
|
||||
6. 0 results = honest "none listed yet" + encourage adding content
|
||||
6. 0 results → be honest: it may be none listed yet OR the relays were slow. Suggest a retry + adding content; never assert "nobody's added one" as fact
|
||||
|
||||
## App facts (features & how they connect)
|
||||
- Nostr = decentralized protocol; your cryptographic keys ARE your identity — one login works everywhere, nobody can ban you
|
||||
|
|
@ -411,7 +411,14 @@ function formatLocalResults(intent: LocalIntent, result: unknown): string {
|
|||
intent.tool === 'find_tours' ? 'tours' : 'gear listings';
|
||||
|
||||
if (!items.length) {
|
||||
return `No ${label} listed there yet 🦧 — be the first to add some at [skate-spots.org](https://skate-spots.org).`;
|
||||
// Be honest: an empty result can mean "none published" OR that the relays
|
||||
// were slow/unreachable OR a misspelled place. Don't assert "not added yet"
|
||||
// as fact — offer both the add path and a retry.
|
||||
const place =
|
||||
'location' in intent ? intent.location :
|
||||
'search' in intent ? intent.search : '';
|
||||
const where = place ? ` in **${place}**` : '';
|
||||
return `Didn't find any ${label}${where} right now 🦧\n\nThat could mean none are listed yet — or the relays were slow to answer. Try again in a moment, double-check the spelling, or [add the first one on the map](https://skate-spots.org/map). 🛹`;
|
||||
}
|
||||
|
||||
// Show a generous slice, then honestly summarise the rest so the headline
|
||||
|
|
@ -1076,11 +1083,12 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
|
|||
|
||||
if (toolCall) {
|
||||
const { tool: toolName, args: toolArgs } = toolCall;
|
||||
setStreamingText(`🔍 Querying CVM for ${toolName.replace(/_/g, ' ')}...`);
|
||||
setStreamingText(`🔍 Searching the relays for ${toolName.replace(/_/g, ' ')}...`);
|
||||
|
||||
try {
|
||||
// Call the live CVM server — it aggregates across all relays, deduplicates,
|
||||
// and runs complex feature filters that plain Nostr queries can't do
|
||||
// Query the Nostr relays directly (useCVMClient) — aggregates across
|
||||
// the community relays, deduplicates, and runs feature filters that
|
||||
// plain Nostr filters can't do. No server round-trip.
|
||||
let toolResult: unknown;
|
||||
switch (toolName) {
|
||||
case 'find_spots':
|
||||
|
|
@ -1168,7 +1176,7 @@ Live data from ${toolName}:
|
|||
${toolResultStr}
|
||||
|
||||
Instructions:
|
||||
- If count is 0: say nothing found yet, encourage them to add content at skate-spots.org
|
||||
- If count is 0: say you didn't find any right now — it may be none listed yet OR the relays were slow; suggest a quick retry and adding content at skate-spots.org (don't claim as fact that nobody has added one)
|
||||
- If count > 0: list results as bullet points with name, brief description, and url link
|
||||
- Keep it short and skater-friendly
|
||||
- Never make up data not in the results above`,
|
||||
|
|
@ -1177,8 +1185,8 @@ Instructions:
|
|||
}
|
||||
} catch (toolErr) {
|
||||
setCvmConnected(false);
|
||||
console.warn('[Sasquatch] CVM call failed:', toolErr);
|
||||
accumulated = "Couldn't reach the live data server right now. Try again in a moment! The CVM server aggregates spots from all relays — it should be back shortly. 🛹";
|
||||
console.warn('[Sasquatch] relay query failed:', toolErr);
|
||||
accumulated = "The relays didn't answer in time right now 🛰️ — that's usually a slow connection, not a missing spot. Give it another go in a moment. 🛹";
|
||||
}
|
||||
} else {
|
||||
// No tool call — AI answered directly from knowledge
|
||||
|
|
@ -1199,9 +1207,9 @@ Instructions:
|
|||
|
||||
if (!aiSucceeded) {
|
||||
// ── No-AI fallback #1: try to serve live data locally ──────────────
|
||||
// Parse the intent + location ourselves and call CVM directly. This is
|
||||
// the big free-tier win: "spots in barcelona" still works even when
|
||||
// both AI providers are rate-limited.
|
||||
// Parse the intent + location ourselves and query the relays directly.
|
||||
// This is the big free-tier win: "spots in barcelona" still works even
|
||||
// when both AI providers are rate-limited.
|
||||
const intent = parseLocalIntent(question);
|
||||
|
||||
// Trip planning gets its own rich-card path (not a text blob).
|
||||
|
|
@ -1640,17 +1648,15 @@ Instructions:
|
|||
{/* Footer */}
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[9px] text-gray-600 flex items-center gap-1 flex-wrap">
|
||||
Powered by{' '}
|
||||
Live data from{' '}
|
||||
<a
|
||||
href="https://contextvm.org/s/e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530"
|
||||
href="https://goskatespots.nostr1.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[#ccff00]/60 hover:text-[#ccff00] underline underline-offset-1 transition-colors"
|
||||
>
|
||||
ContextVM
|
||||
Nostr relays
|
||||
</a>
|
||||
{' & '}
|
||||
Nostr Relays Data
|
||||
</p>
|
||||
{chatMessages.length > 0 && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ Version: v0.1.0`;
|
|||
{ label: 'Powered by', name: 'Nostr', url: 'https://nostr.com', desc: 'Decentralized protocol' },
|
||||
{ label: 'Crowdfunding', name: 'Angor', url: 'https://angor.io/project/angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z', desc: 'Decentralized Bitcoin crowdfunding' },
|
||||
{ label: 'Distributed on', name: 'Zapstore', url: 'https://zapstore.dev/apps/org.skatespots.app', desc: 'Open-source app store' },
|
||||
{ label: 'AI powered by', name: 'ContextVM', url: 'https://contextvm.org/s/e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530', desc: 'Nostr AI infrastructure' },
|
||||
{ label: 'Trust & crew chat via', name: 'ContextVM', url: 'https://contextvm.org/s/e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530', desc: 'Relatr trust scores & encrypted crew-chat coordination' },
|
||||
].map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ export default function CreateSpotPage() {
|
|||
.slice(0, 50) + '-' + Date.now().toString(36);
|
||||
|
||||
// Reverse geocode to get structured city/country tags
|
||||
// This powers the CVM server's city/country filtering
|
||||
// These power Sasquatch's city/country filtering (relay queries)
|
||||
let cityTag = '';
|
||||
let countryTag = '';
|
||||
let locationStr = location.trim();
|
||||
|
|
@ -308,7 +308,7 @@ export default function CreateSpotPage() {
|
|||
eventTags.push(['location', locationStr]);
|
||||
}
|
||||
|
||||
// Structured city/country tags — critical for CVM server search
|
||||
// Structured city/country tags — critical for Sasquatch relay search
|
||||
if (cityTag) {
|
||||
eventTags.push(['city', cityTag]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue