Spaces:
Running
Running
Upload 5 files
Browse files- chat-engine.jsx +20 -13
- debug-panel.jsx +33 -2
chat-engine.jsx
CHANGED
|
@@ -36,18 +36,22 @@ const FALLBACKS = [
|
|
| 36 |
|
| 37 |
const INTENTS = [
|
| 38 |
{
|
|
|
|
| 39 |
patterns: [/\b(hi|hello|hey)\b/i, /good (morning|afternoon|evening)/i],
|
| 40 |
response: "Hi! I'm Fetch Pup. Ask me something about dogs, or try the suggestions below.",
|
| 41 |
},
|
| 42 |
{
|
|
|
|
| 43 |
patterns: [/how are you/i, /how('s| is) it going/i, /what'?s up/i],
|
| 44 |
response: "Tail's wagging, thanks for asking! Got a dog question for me?",
|
| 45 |
},
|
| 46 |
{
|
|
|
|
| 47 |
patterns: [/what can you do/i, /what do you (know|do)/i, /\bhelp\b/i],
|
| 48 |
response: "I know a bunch of dog facts. Ask a question or try the suggestions — I'll do my best.",
|
| 49 |
},
|
| 50 |
{
|
|
|
|
| 51 |
patterns: [/who are you/i, /what are you/i],
|
| 52 |
response: "I'm Fetch Pup — a static chatbot demo. No model running, just intent matching and keyword lookup. But I know my dogs!",
|
| 53 |
},
|
|
@@ -56,7 +60,7 @@ const INTENTS = [
|
|
| 56 |
function matchIntent(query) {
|
| 57 |
const q = query || "";
|
| 58 |
for (const intent of INTENTS) {
|
| 59 |
-
if (intent.patterns.some(p => p.test(q))) return intent
|
| 60 |
}
|
| 61 |
return null;
|
| 62 |
}
|
|
@@ -121,7 +125,7 @@ function buildPrompt(history, userInput, fact, personality) {
|
|
| 121 |
|
| 122 |
async function generateReply({ userInput, history, backend, personality }) {
|
| 123 |
const intent = matchIntent(userInput);
|
| 124 |
-
if (intent) return { text: intent, fact: null };
|
| 125 |
|
| 126 |
const fact = retrieveFact(userInput);
|
| 127 |
|
|
@@ -131,9 +135,9 @@ async function generateReply({ userInput, history, backend, personality }) {
|
|
| 131 |
const raw = await window.claude.complete(prompt);
|
| 132 |
const text = (raw || "").trim().replace(/^Bot:\s*/i, "").split("\nUser:")[0].trim();
|
| 133 |
if (!text || text.split(/\s+/).length <= 2) {
|
| 134 |
-
return { text: pickFallback(), fact };
|
| 135 |
}
|
| 136 |
-
return { text, fact };
|
| 137 |
} catch (err) {
|
| 138 |
console.warn("Fetch: claude backend failed, falling back.", err);
|
| 139 |
// fall through to local path
|
|
@@ -141,8 +145,8 @@ async function generateReply({ userInput, history, backend, personality }) {
|
|
| 141 |
}
|
| 142 |
|
| 143 |
// Local / offline / HF-static path: keyword fact wins, else fallback.
|
| 144 |
-
if (fact) return { text: fact.fact, fact };
|
| 145 |
-
return { text: pickFallback(), fact };
|
| 146 |
}
|
| 147 |
|
| 148 |
// Hook: owns conversation state + send/clear + rotating suggestions.
|
|
@@ -171,12 +175,14 @@ function useFetchChat({ backend = "claude", personality = "subtle" } = {}) {
|
|
| 171 |
setPending(true);
|
| 172 |
|
| 173 |
const t0 = performance.now();
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
history: priorHistory,
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
| 180 |
const ms = Math.round(performance.now() - t0);
|
| 181 |
|
| 182 |
setMessages(prev => prev.map(m =>
|
|
@@ -185,7 +191,7 @@ function useFetchChat({ backend = "claude", personality = "subtle" } = {}) {
|
|
| 185 |
: m
|
| 186 |
));
|
| 187 |
setDebug(prev => [
|
| 188 |
-
{ t: Date.now(), user: userInput, reply: text, fact, ms, backend },
|
| 189 |
...prev,
|
| 190 |
].slice(0, 20));
|
| 191 |
setPending(false);
|
|
@@ -228,4 +234,5 @@ Object.assign(window, {
|
|
| 228 |
retrieveFact,
|
| 229 |
FETCH_KNOWLEDGE_BASE: KNOWLEDGE_BASE,
|
| 230 |
FETCH_SUGGESTIONS: SUGGESTIONS,
|
|
|
|
| 231 |
});
|
|
|
|
| 36 |
|
| 37 |
const INTENTS = [
|
| 38 |
{
|
| 39 |
+
label: "greeting",
|
| 40 |
patterns: [/\b(hi|hello|hey)\b/i, /good (morning|afternoon|evening)/i],
|
| 41 |
response: "Hi! I'm Fetch Pup. Ask me something about dogs, or try the suggestions below.",
|
| 42 |
},
|
| 43 |
{
|
| 44 |
+
label: "small talk",
|
| 45 |
patterns: [/how are you/i, /how('s| is) it going/i, /what'?s up/i],
|
| 46 |
response: "Tail's wagging, thanks for asking! Got a dog question for me?",
|
| 47 |
},
|
| 48 |
{
|
| 49 |
+
label: "capability",
|
| 50 |
patterns: [/what can you do/i, /what do you (know|do)/i, /\bhelp\b/i],
|
| 51 |
response: "I know a bunch of dog facts. Ask a question or try the suggestions — I'll do my best.",
|
| 52 |
},
|
| 53 |
{
|
| 54 |
+
label: "identity",
|
| 55 |
patterns: [/who are you/i, /what are you/i],
|
| 56 |
response: "I'm Fetch Pup — a static chatbot demo. No model running, just intent matching and keyword lookup. But I know my dogs!",
|
| 57 |
},
|
|
|
|
| 60 |
function matchIntent(query) {
|
| 61 |
const q = query || "";
|
| 62 |
for (const intent of INTENTS) {
|
| 63 |
+
if (intent.patterns.some(p => p.test(q))) return intent;
|
| 64 |
}
|
| 65 |
return null;
|
| 66 |
}
|
|
|
|
| 125 |
|
| 126 |
async function generateReply({ userInput, history, backend, personality }) {
|
| 127 |
const intent = matchIntent(userInput);
|
| 128 |
+
if (intent) return { text: intent.response, fact: null, source: "intent", label: intent.label };
|
| 129 |
|
| 130 |
const fact = retrieveFact(userInput);
|
| 131 |
|
|
|
|
| 135 |
const raw = await window.claude.complete(prompt);
|
| 136 |
const text = (raw || "").trim().replace(/^Bot:\s*/i, "").split("\nUser:")[0].trim();
|
| 137 |
if (!text || text.split(/\s+/).length <= 2) {
|
| 138 |
+
return { text: pickFallback(), fact, source: "fallback" };
|
| 139 |
}
|
| 140 |
+
return { text, fact, source: "model" };
|
| 141 |
} catch (err) {
|
| 142 |
console.warn("Fetch: claude backend failed, falling back.", err);
|
| 143 |
// fall through to local path
|
|
|
|
| 145 |
}
|
| 146 |
|
| 147 |
// Local / offline / HF-static path: keyword fact wins, else fallback.
|
| 148 |
+
if (fact) return { text: fact.fact, fact, source: "fact" };
|
| 149 |
+
return { text: pickFallback(), fact: null, source: "fallback" };
|
| 150 |
}
|
| 151 |
|
| 152 |
// Hook: owns conversation state + send/clear + rotating suggestions.
|
|
|
|
| 175 |
setPending(true);
|
| 176 |
|
| 177 |
const t0 = performance.now();
|
| 178 |
+
let result;
|
| 179 |
+
try {
|
| 180 |
+
result = await generateReply({ userInput, history: priorHistory, backend, personality });
|
| 181 |
+
} catch (err) {
|
| 182 |
+
console.error("Fetch: generateReply failed.", err);
|
| 183 |
+
result = { text: "Something went wrong. Please try again.", fact: null, source: "error" };
|
| 184 |
+
}
|
| 185 |
+
const { text, fact, source, label } = result;
|
| 186 |
const ms = Math.round(performance.now() - t0);
|
| 187 |
|
| 188 |
setMessages(prev => prev.map(m =>
|
|
|
|
| 191 |
: m
|
| 192 |
));
|
| 193 |
setDebug(prev => [
|
| 194 |
+
{ t: Date.now(), user: userInput, reply: text, fact, source, label, ms, backend },
|
| 195 |
...prev,
|
| 196 |
].slice(0, 20));
|
| 197 |
setPending(false);
|
|
|
|
| 234 |
retrieveFact,
|
| 235 |
FETCH_KNOWLEDGE_BASE: KNOWLEDGE_BASE,
|
| 236 |
FETCH_SUGGESTIONS: SUGGESTIONS,
|
| 237 |
+
FETCH_INTENTS: INTENTS,
|
| 238 |
});
|
debug-panel.jsx
CHANGED
|
@@ -41,6 +41,7 @@ function DebugPanel({ debug, onClose, palette, font }) {
|
|
| 41 |
}}>
|
| 42 |
{[
|
| 43 |
{ id: "turns", label: "turns" },
|
|
|
|
| 44 |
{ id: "keywords", label: "keywords" },
|
| 45 |
{ id: "about", label: "about" },
|
| 46 |
].map(t => (
|
|
@@ -57,6 +58,7 @@ function DebugPanel({ debug, onClose, palette, font }) {
|
|
| 57 |
|
| 58 |
<div style={{ flex: 1, overflowY: "auto", padding: 12 }}>
|
| 59 |
{tab === "turns" && <DebugLog debug={debug} palette={palette} />}
|
|
|
|
| 60 |
{tab === "keywords" && <DebugKB palette={palette} />}
|
| 61 |
{tab === "about" && <DebugAbout palette={palette} />}
|
| 62 |
</div>
|
|
@@ -92,8 +94,17 @@ function DebugLog({ debug, palette }) {
|
|
| 92 |
<span style={{ color: palette.accent }}>reply> </span>
|
| 93 |
{d.reply}
|
| 94 |
</div>
|
| 95 |
-
<div style={{ color: palette.dim, fontSize:
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
</div>
|
| 98 |
</div>
|
| 99 |
))}
|
|
@@ -121,6 +132,26 @@ function DebugKB({ palette }) {
|
|
| 121 |
);
|
| 122 |
}
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
function DebugAbout({ palette }) {
|
| 125 |
return (
|
| 126 |
<div style={{ lineHeight: 1.6, color: palette.fg, display: "flex", flexDirection: "column", gap: 10 }}>
|
|
|
|
| 41 |
}}>
|
| 42 |
{[
|
| 43 |
{ id: "turns", label: "turns" },
|
| 44 |
+
{ id: "intents", label: "intents" },
|
| 45 |
{ id: "keywords", label: "keywords" },
|
| 46 |
{ id: "about", label: "about" },
|
| 47 |
].map(t => (
|
|
|
|
| 58 |
|
| 59 |
<div style={{ flex: 1, overflowY: "auto", padding: 12 }}>
|
| 60 |
{tab === "turns" && <DebugLog debug={debug} palette={palette} />}
|
| 61 |
+
{tab === "intents" && <DebugIntents palette={palette} />}
|
| 62 |
{tab === "keywords" && <DebugKB palette={palette} />}
|
| 63 |
{tab === "about" && <DebugAbout palette={palette} />}
|
| 64 |
</div>
|
|
|
|
| 94 |
<span style={{ color: palette.accent }}>reply> </span>
|
| 95 |
{d.reply}
|
| 96 |
</div>
|
| 97 |
+
<div style={{ color: palette.dim, fontSize: 11 }}>
|
| 98 |
+
{"source: "}
|
| 99 |
+
<span style={{
|
| 100 |
+
color: d.source === "error" ? "#ff6b6b"
|
| 101 |
+
: d.source === "fallback" ? palette.dim
|
| 102 |
+
: palette.accent,
|
| 103 |
+
}}>
|
| 104 |
+
{d.source === "fact" ? `fact · ${d.fact?.keyword}`
|
| 105 |
+
: d.source === "intent" ? `intent · ${d.label}`
|
| 106 |
+
: d.source || "unknown"}
|
| 107 |
+
</span>
|
| 108 |
</div>
|
| 109 |
</div>
|
| 110 |
))}
|
|
|
|
| 132 |
);
|
| 133 |
}
|
| 134 |
|
| 135 |
+
function DebugIntents({ palette }) {
|
| 136 |
+
return (
|
| 137 |
+
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
| 138 |
+
<div style={{ color: palette.dim, marginBottom: 4 }}>
|
| 139 |
+
{FETCH_INTENTS.length} intents · first match wins
|
| 140 |
+
</div>
|
| 141 |
+
{FETCH_INTENTS.map(intent => (
|
| 142 |
+
<div key={intent.label} style={{
|
| 143 |
+
border: `1px solid ${palette.muted}`,
|
| 144 |
+
borderRadius: 6,
|
| 145 |
+
padding: 8,
|
| 146 |
+
}}>
|
| 147 |
+
<div style={{ color: palette.accent, marginBottom: 3 }}>{intent.label}</div>
|
| 148 |
+
<div style={{ color: palette.fg, lineHeight: 1.4 }}>{intent.response}</div>
|
| 149 |
+
</div>
|
| 150 |
+
))}
|
| 151 |
+
</div>
|
| 152 |
+
);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
function DebugAbout({ palette }) {
|
| 156 |
return (
|
| 157 |
<div style={{ lineHeight: 1.6, color: palette.fg, display: "flex", flexDirection: "column", gap: 10 }}>
|