gnumanth commited on
Commit
3c37047
ยท
1 Parent(s): 905d68e

fix: add search_xkcd_by_topic tool for topics like Python (#353), Git (#1597), and SQL (#327)

Browse files
Files changed (1) hide show
  1. index.html +72 -11
index.html CHANGED
@@ -408,7 +408,7 @@
408
  <a class="badge peach" href="https://xkcd.hemanth.deno.net/" target="_blank">๐ŸŒ xkcd.hemanth.deno.net</a>
409
  </div>
410
  <h1>PhoneLLM XKCD Voice Agent</h1>
411
- <p class="sub">Soft pastel voice interface with high-fidelity on-device neural speech & live XKCD tool calls.</p>
412
  </header>
413
 
414
  <main class="main-grid">
@@ -429,16 +429,17 @@
429
  </div>
430
 
431
  <div class="chips-row">
432
- <button class="chip" onclick="sendQuery('Show me the latest XKCD comic')">โœจ Latest Comic</button>
433
- <button class="chip" onclick="sendQuery('Give me a random XKCD comic and explain it')">๐ŸŽฒ Random Comic</button>
434
  <button class="chip" onclick="sendQuery('What is XKCD comic 327 (Little Bobby Tables)?')">๐Ÿ’ป Little Bobby Tables (#327)</button>
435
- <button class="chip" onclick="sendQuery('Show comic 300')">๐Ÿ“ฑ Comic #300</button>
 
 
436
  </div>
437
 
438
  <div id="chat-box" class="chat-box"></div>
439
 
440
  <div class="input-row">
441
- <input id="user-input" type="text" placeholder="Speak or type (e.g. 'Get a random comic')..." />
442
  <button id="send-btn" class="btn-send">Send</button>
443
  </div>
444
  </div>
@@ -469,6 +470,43 @@
469
  let selectedVoice = null;
470
  let availableVoices = [];
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  // Initialize High-Quality On-Device TTS Voices
473
  function loadVoices() {
474
  if (!('speechSynthesis' in window)) return;
@@ -477,7 +515,6 @@
477
 
478
  voiceSelect.innerHTML = '';
479
 
480
- // Filter for premium / natural English voices (Samantha, Daniel, Siri, Google US, Natural, etc.)
481
  const preferred = availableVoices.filter(v =>
482
  v.lang.startsWith('en') &&
483
  (v.name.includes('Natural') || v.name.includes('Neural') || v.name.includes('Samantha') || v.name.includes('Google') || v.name.includes('Siri') || v.name.includes('Daniel') || v.name.includes('Karen') || v.name.includes('Alex'))
@@ -492,7 +529,6 @@
492
  voiceSelect.appendChild(opt);
493
  });
494
 
495
- // Default to the highest quality natural neural voice
496
  selectedVoice = voicesToShow[0] || availableVoices[0];
497
  voiceSelect.onchange = () => {
498
  selectedVoice = voicesToShow[voiceSelect.value] || availableVoices[0];
@@ -549,6 +585,16 @@
549
  return json.data || json;
550
  }
551
 
 
 
 
 
 
 
 
 
 
 
552
  // PhoneLLM Conversational & Tool Calling Engine
553
  async function processPhoneLLMTurn(userText) {
554
  statusBadge.textContent = "โšก PhoneLLM Reasoning...";
@@ -561,7 +607,22 @@
561
 
562
  const numMatch = lower.match(/\b(?:comic|number|#)?\s*(\d+)\b/);
563
 
564
- if (lower.includes("random") || lower.includes("any comic")) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  toolName = "get_random_xkcd()";
566
  statusBadge.textContent = "โš™๏ธ Executing get_random_xkcd...";
567
  comic = await toolGetRandomXkcd();
@@ -572,13 +633,13 @@
572
  statusBadge.textContent = `โš™๏ธ Executing get_xkcd_by_number(${comicNum})...`;
573
  comic = await toolGetXkcdByNumber(comicNum);
574
  replyText = `Here is XKCD comic #${comic.num}: "${comic.title}". The punchline alt text says: "${comic.alt}"`;
575
- } else if (lower.includes("latest") || lower.includes("newest") || lower.includes("today") || lower.includes("recent") || lower.includes("comic") || lower.includes("xkcd")) {
576
  toolName = "get_latest_xkcd()";
577
  statusBadge.textContent = "โš™๏ธ Executing get_latest_xkcd...";
578
  comic = await toolGetLatestXkcd();
579
  replyText = `Here is the latest XKCD comic: #${comic.num} titled "${comic.title}". The alt text says: "${comic.alt}"`;
580
  } else {
581
- replyText = `I am your XKCD voice assistant powered by PhoneLLM Alpha 1. You can ask me to fetch the latest comic, get a random comic, or look up any specific comic number like #327!`;
582
  }
583
 
584
  addMessage(replyText, "agent", toolName);
@@ -676,7 +737,7 @@
676
 
677
  // Initial greeting
678
  setTimeout(() => {
679
- addMessage("Hello! I am your Pipecat voice assistant powered by PhoneLLM Alpha 1. Tap the glowing pastel orb or select a prompt below to look up XKCD comics!", "agent");
680
  }, 400);
681
  </script>
682
  </body>
 
408
  <a class="badge peach" href="https://xkcd.hemanth.deno.net/" target="_blank">๐ŸŒ xkcd.hemanth.deno.net</a>
409
  </div>
410
  <h1>PhoneLLM XKCD Voice Agent</h1>
411
+ <p class="sub">Soft pastel voice interface with high-fidelity on-device neural speech & live XKCD topic search.</p>
412
  </header>
413
 
414
  <main class="main-grid">
 
429
  </div>
430
 
431
  <div class="chips-row">
432
+ <button class="chip" onclick="sendQuery('Show me a comic on Python programming language')">๐Ÿ Python Comic</button>
 
433
  <button class="chip" onclick="sendQuery('What is XKCD comic 327 (Little Bobby Tables)?')">๐Ÿ’ป Little Bobby Tables (#327)</button>
434
+ <button class="chip" onclick="sendQuery('Show me a comic about Git version control')">๐ŸŒฟ Git Comic</button>
435
+ <button class="chip" onclick="sendQuery('Show me the latest XKCD comic')">โœจ Latest Comic</button>
436
+ <button class="chip" onclick="sendQuery('Give me a random XKCD comic')">๐ŸŽฒ Random Comic</button>
437
  </div>
438
 
439
  <div id="chat-box" class="chat-box"></div>
440
 
441
  <div class="input-row">
442
+ <input id="user-input" type="text" placeholder="Speak or type (e.g. 'Show me a comic on Python')..." />
443
  <button id="send-btn" class="btn-send">Send</button>
444
  </div>
445
  </div>
 
470
  let selectedVoice = null;
471
  let availableVoices = [];
472
 
473
+ // Topic Knowledge Index for iconic XKCD comics
474
+ const TOPIC_INDEX = {
475
+ "python": 353,
476
+ "antigravity": 353,
477
+ "sql": 327,
478
+ "database": 327,
479
+ "injection": 327,
480
+ "bobby tables": 327,
481
+ "little bobby tables": 327,
482
+ "git": 1597,
483
+ "version control": 1597,
484
+ "tar": 1168,
485
+ "zip": 1168,
486
+ "archive": 1168,
487
+ "regex": 208,
488
+ "regular expression": 208,
489
+ "password": 936,
490
+ "correct horse": 936,
491
+ "machine learning": 1838,
492
+ "ai": 1838,
493
+ "artificial intelligence": 1838,
494
+ "compiling": 303,
495
+ "sword fight": 303,
496
+ "standards": 927,
497
+ "facebook": 300,
498
+ "social media": 300,
499
+ "wisdom of the ancients": 979,
500
+ "sudo": 149,
501
+ "sandwich": 149,
502
+ "lisp": 224,
503
+ "c++": 435,
504
+ "rust": 2248,
505
+ "javascript": 2248,
506
+ "linux": 272,
507
+ "kernel": 272
508
+ };
509
+
510
  // Initialize High-Quality On-Device TTS Voices
511
  function loadVoices() {
512
  if (!('speechSynthesis' in window)) return;
 
515
 
516
  voiceSelect.innerHTML = '';
517
 
 
518
  const preferred = availableVoices.filter(v =>
519
  v.lang.startsWith('en') &&
520
  (v.name.includes('Natural') || v.name.includes('Neural') || v.name.includes('Samantha') || v.name.includes('Google') || v.name.includes('Siri') || v.name.includes('Daniel') || v.name.includes('Karen') || v.name.includes('Alex'))
 
529
  voiceSelect.appendChild(opt);
530
  });
531
 
 
532
  selectedVoice = voicesToShow[0] || availableVoices[0];
533
  voiceSelect.onchange = () => {
534
  selectedVoice = voicesToShow[voiceSelect.value] || availableVoices[0];
 
585
  return json.data || json;
586
  }
587
 
588
+ async function toolSearchXkcdByTopic(topic) {
589
+ const cleanTopic = topic.toLowerCase().trim();
590
+ for (const [key, num] of Object.entries(TOPIC_INDEX)) {
591
+ if (cleanTopic.includes(key) || key.includes(cleanTopic)) {
592
+ return { comic: await toolGetXkcdByNumber(num), topic: key };
593
+ }
594
+ }
595
+ return { comic: await toolGetRandomXkcd(), topic: cleanTopic };
596
+ }
597
+
598
  // PhoneLLM Conversational & Tool Calling Engine
599
  async function processPhoneLLMTurn(userText) {
600
  statusBadge.textContent = "โšก PhoneLLM Reasoning...";
 
607
 
608
  const numMatch = lower.match(/\b(?:comic|number|#)?\s*(\d+)\b/);
609
 
610
+ // Check for topic queries first (e.g. "comic on Python", "about git", "database comic")
611
+ let matchedTopic = null;
612
+ for (const key of Object.keys(TOPIC_INDEX)) {
613
+ if (lower.includes(key)) {
614
+ matchedTopic = key;
615
+ break;
616
+ }
617
+ }
618
+
619
+ if (matchedTopic && (lower.includes("about") || lower.includes("on") || lower.includes("show") || lower.includes("comic") || lower.includes("tell") || lower.includes("find") || lower.includes("give") || lower.includes("python") || lower.includes("git") || lower.includes("sql"))) {
620
+ toolName = `search_xkcd_by_topic(topic="${matchedTopic}")`;
621
+ statusBadge.textContent = `โš™๏ธ Executing search_xkcd_by_topic("${matchedTopic}")...`;
622
+ const res = await toolSearchXkcdByTopic(matchedTopic);
623
+ comic = res.comic;
624
+ replyText = `Here is the iconic XKCD comic on ${matchedTopic.toUpperCase()}: #${comic.num} titled "${comic.title}". The punchline alt text says: "${comic.alt}"`;
625
+ } else if (lower.includes("random") || lower.includes("any comic")) {
626
  toolName = "get_random_xkcd()";
627
  statusBadge.textContent = "โš™๏ธ Executing get_random_xkcd...";
628
  comic = await toolGetRandomXkcd();
 
633
  statusBadge.textContent = `โš™๏ธ Executing get_xkcd_by_number(${comicNum})...`;
634
  comic = await toolGetXkcdByNumber(comicNum);
635
  replyText = `Here is XKCD comic #${comic.num}: "${comic.title}". The punchline alt text says: "${comic.alt}"`;
636
+ } else if (lower.includes("latest") || lower.includes("newest") || lower.includes("today") || lower.includes("recent")) {
637
  toolName = "get_latest_xkcd()";
638
  statusBadge.textContent = "โš™๏ธ Executing get_latest_xkcd...";
639
  comic = await toolGetLatestXkcd();
640
  replyText = `Here is the latest XKCD comic: #${comic.num} titled "${comic.title}". The alt text says: "${comic.alt}"`;
641
  } else {
642
+ replyText = `I am your XKCD voice assistant powered by PhoneLLM Alpha 1. You can ask me for comics on Python, Git, SQL/Bobby Tables, or ask for the latest or random comics!`;
643
  }
644
 
645
  addMessage(replyText, "agent", toolName);
 
737
 
738
  // Initial greeting
739
  setTimeout(() => {
740
+ addMessage("Hello! I am your Pipecat voice assistant powered by PhoneLLM Alpha 1. Try asking: 'Show me a comic on Python' or 'What is Little Bobby Tables?'", "agent");
741
  }, 400);
742
  </script>
743
  </body>