UNPKG

nlweb-js-client

Version:

A streaming chat interface client library

3 lines (2 loc) 18.8 kB
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NLWebJsClient={})}(this,(function(e){"use strict";class t{constructor(e,t={}){this.url=e,this.options=t,this.maxRetries=t.maxRetries||3,this.retryCount=0,this.eventSource=null,this.isStopped=!1}connect(e){this.isStopped||(this.eventSource=new EventSource(this.url),this.eventSource.chatInterface=e,this.eventSource.onopen=()=>{this.retryCount=0},this.eventSource.onerror=e=>{if(this.eventSource.readyState===EventSource.CLOSED)if(console.log("Connection was closed"),this.retryCount<this.maxRetries){this.retryCount++,console.log(`Retry attempt ${this.retryCount} of ${this.maxRetries}`);const e=Math.min(1e3*Math.pow(2,this.retryCount),1e4);setTimeout((()=>this.connect()),e)}else console.log("Max retries reached, stopping reconnection attempts"),this.stop()},this.eventSource.onmessage=function(e){if(this.chatInterface.dotsStillThere){this.chatInterface.handleFirstMessage(e);const t=document.createElement("div");t.className="message assistant-message";const n=document.createElement("div");n.className="message-bubble",t.appendChild(n),this.chatInterface.bubble=n,this.chatInterface.messagesArea.appendChild(t),this.chatInterface.currentItems=[],this.chatInterface.thisRoundRemembered=null}const t=JSON.parse(e.data);if(this.query_id&&t.query_id&&this.query_id!==t.query_id)console.log("Query ID mismatch, ignoring message");else if(t&&"query_analysis"==t.message_type)this.chatInterface.itemToRemember.push(t.item_to_remember),this.chatInterface.decontextualizedQuery=t.decontextualized_query,this.chatInterface.possiblyAnnotateUserQuery(this.chatInterface,t.decontextualized_query),this.chatInterface.itemToRemember&&this.chatInterface.memoryMessage(t.item_to_remember,this.chatInterface);else if(t&&"remember"==t.message_type)this.chatInterface.memoryMessage(t.message,this.chatInterface);else if(t&&"asking_sites"==t.message_type)this.chatInterface.sourcesMessage=this.chatInterface.createIntermediateMessageHtml(t.message),this.chatInterface.bubble.appendChild(this.chatInterface.sourcesMessage);else if(t&&"site_is_irrelevant_to_query"==t.message_type)this.chatInterface.siteIsIrrelevantToQuery(t.message,this.chatInterface);else if(t&&"ask_user"==t.message_type)this.chatInterface.askUserMessage(t.message,this.chatInterface);else if(t&&"item_details"==t.message_type)this.chatInterface.itemDetailsMessage(t.message,this.chatInterface);else if(t&&"result_batch"==t.message_type){for(const e of t.results){const t=this.chatInterface.createJsonItemHtml(e);this.chatInterface.currentItems.push([e,t]),this.chatInterface.bubble.appendChild(t),this.chatInterface.num_results_sent++}this.chatInterface.resortResults(this.chatInterface)}else if(t&&"intermediate_message"==t.message_type)this.chatInterface.bubble.appendChild(this.chatInterface.createIntermediateMessageHtml(t.message));else if(t&&"summary"==t.message_type)this.chatInterface.thisRoundSummary=this.chatInterface.createIntermediateMessageHtml(t.message),this.chatInterface.resortResults(this.chatInterface);else if(t&&"nlws"==t.message_type){for(;this.chatInterface.bubble.firstChild;)this.chatInterface.bubble.removeChild(this.chatInterface.bubble.firstChild);this.chatInterface.itemDetailsMessage(t.answer,this.chatInterface);for(const e of t.items){const t=this.chatInterface.createJsonItemHtml(e);this.chatInterface.currentItems.push([e,t]),this.chatInterface.bubble.appendChild(t)}}else t&&"complete"==t.message_type&&(this.chatInterface.resortResults(this.chatInterface),this.chatInterface.scrollDiv.scrollIntoView(),this.close())})}stop(){this.isStopped=!0,this.eventSource&&(this.eventSource.close(),this.eventSource=null)}reset(){this.retryCount=0,this.isStopped=!1,this.stop(),this.connect()}}function n(e){if(!e||"string"!=typeof e)return"#";const t=e.trim();try{if(/^(javascript|data|vbscript|file):/i.test(t))return"#";const e=new URL(t,window.location.origin);return["http:","https:"].includes(e.protocol)?e.toString():"#"}catch(t){return console.warn("Invalid URL detected and sanitized:",e),"#"}}function s(e){if(!e)return"";if("string"!=typeof e)return String(e);const t=document.createElement("textarea");return t.textContent=e,t.value}function a(e){const t=document.createElement("span");return t.textContent=" "+e+" ",t}function r(e,t="dropdown"){e.container=document.getElementById("chat-container"),"dropdown"==t&&function(e){const t=document.createElement("div");e.selector=t,t.className="site-selector";const n=document.createElement("select");e.siteSelect=n,["imdb","nytimes","alltrails","allbirds","seriouseats","npr podcasts","backcountry","bc_product","neurips","zillow","tripadvisor","woksoflife","cheftariq","hebbarskitchen","latam_recipes","spruce","med podcast","all"].forEach((e=>{const t=document.createElement("option");t.value=e,t.textContent=e,n.appendChild(t)})),t.appendChild(a("Site")),t.appendChild(n),n.addEventListener("change",(()=>{e.resetChatState()}));const s=document.createElement("select");e.generateModeSelect=s,t.appendChild(a("Mode")),["list","summarize","generate"].forEach((e=>{const t=document.createElement("option");t.value=e,t.textContent=e,s.appendChild(t)})),s.addEventListener("change",(()=>{e.generate_mode=s.value,e.resetChatState()})),t.appendChild(s);const r=document.createElement("span");r.innerHTML='<img src="/html/clear.jpeg" width="16" height="16" style="vertical-align: middle; cursor: pointer; margin-left: 8px;">',r.title="Clear chat history",r.addEventListener("click",(()=>{e.resetChatState()})),t.appendChild(r);const i=document.createElement("span");i.innerHTML='<img src="/html/debug.png" width="16" height="16" style="vertical-align: middle; cursor: pointer; margin-left: 8px;">',i.title="Debug",i.addEventListener("click",(()=>{e.debug_mode?(e.debug_mode=!1,e.bubble.innerHTML="",e.resortResults(e)):(e.debug_mode=!0,e.bubble.innerHTML=e.createDebugString())})),t.appendChild(i);const o=document.createElement("div");o.id="context_url_div",o.style.display="none",o.style.marginTop="8px";const c=document.createElement("input");c.type="text",c.id="context_url",c.placeholder="Enter Context URL",c.style.width="200px",o.appendChild(a("Context URL")),o.appendChild(c),t.appendChild(o),e.context_url=c,e.container.appendChild(t)}(e),e.messagesArea=document.createElement("div"),e.messagesArea.className="dropdown"==t?"messages":"messages_full",e.inputArea=document.createElement("div"),e.inputArea.className="dropdown"==t?"input-area":"input-area_full",e.input=document.createElement("textarea"),e.input.className="message-input",e.input.placeholder="Type your message...",e.sendButton=document.createElement("button"),e.sendButton.className="send-button",e.sendButton.textContent="Send",e.inputArea.appendChild(e.input),e.inputArea.appendChild(e.sendButton),e.container.appendChild(e.messagesArea),e.container.appendChild(e.inputArea)}function i(e){const t=document.createElement("span");return t.textContent=e,t.style.fontSize="0.85em",t}function o(e,t,n=!1){const s=document.createElement("div");t.appendChild(document.createElement("br"));const a=i(e.explanation);return a.className="item-details-message",s.appendChild(a),t.appendChild(s),s}function c(e,t){const a=document.createElement("div");a.style.display="flex",a.style.marginBottom="1em",a.style.gap="1em";const r=document.createElement("div");r.style.flex="1";const c=document.createElement("div");c.style.display="flex",c.style.alignItems="center",c.style.gap="0.5em",c.style.marginBottom="0.5em";const l=document.createElement("a"),d=e.url?n(e.url):"#";l.href=d,"#"===d||d.startsWith(window.location.origin)||(l.rel="noopener noreferrer",l.target="_blank");const h=function(e){return e.name?e.name:e.schema_object&&e.schema_object.keywords?e.schema_object.keywords:e.url}(e);l.textContent=s(`${h}`),l.style.fontWeight="600",l.style.textDecoration="none",l.style.color="#2962ff",c.appendChild(l);const m=document.createElement("span"),u=document.createElement("img");u.src=n("static/images/info.png"),u.width=16,u.height=16,u.alt="Info",m.appendChild(u),m.style.fontSize="0.5em",m.style.position="relative",m.title=`${e.explanation||""} (score=${e.score||0}) (Ranking time=${e.time||0})`,c.appendChild(m),r.appendChild(c);const p=document.createElement("div");if(p.textContent=e.description||"",p.style.fontSize="0.9em",r.appendChild(p),"nlwebsearch"==t.display_mode){document.createElement("div");const t=document.createElement("a"),a=e.siteUrl?n(e.siteUrl):"#";t.href=a,"#"===a||a.startsWith(window.location.origin)||(t.rel="noopener noreferrer",t.target="_blank"),t.textContent=s(e.site||""),t.style.fontSize="0.9em",t.style.textDecoration="none",t.style.color="#2962ff",t.style.fontWeight="500",t.style.padding="8px 0",t.style.display="inline-block",r.appendChild(t)}!function(e,t){if(!e.schema_object)return;const n=e.schema_object["@type"];if("PodcastEpisode"!=n){if(["SingleFamilyResidence","Apartment","Townhouse","House","Condominium","RealEstateListing"].includes(n)){const n=o(e,t,!0),s=e.schema_object.price,a=e.schema_object.address,r=e.schema_object.numberOfRooms,c=e.schema_object.numberOfBathroomsTotal,l=e.schema_object.floorSize?.value;let d=s;"object"==typeof s&&(d=s.price||s.value||s,d=1e5*Math.round(d/1e5),d=d.toLocaleString("en-US")),a?.streetAddress&&a?.addressLocality&&(n.appendChild(i(a.streetAddress+", "+a.addressLocality)),n.appendChild(document.createElement("br"))),r&&c&&l&&(n.appendChild(i(`${r} bedrooms, ${c} bathrooms, ${l} sqft`)),n.appendChild(document.createElement("br"))),d&&n.appendChild(i(`Listed at ${d}`))}}else o(e,t,!0)}(e,r);const g=document.createElement("div");g.style.display="flex",g.style.gap="0.5em",g.style.marginTop="0.5em";const f=document.createElement("span");f.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="#D3D3D3">\n <path d="M2 20h2c.55 0 1-.45 1-1v-9c0-.55-.45-1-1-1H2v11zm19.83-7.12c.11-.25.17-.52.17-.8V11c0-1.1-.9-2-2-2h-5.5l.92-4.65c.05-.22.02-.46-.08-.66-.23-.45-.52-.86-.88-1.22L14 2 7.59 8.41C7.21 8.79 7 9.3 7 9.83v7.84C7 18.95 8.05 20 9.34 20h8.11c.7 0 1.36-.37 1.72-.97l2.66-6.15z"/>\n </svg>',f.style.fontSize="0.8em",f.style.cursor="pointer";const y=document.createElement("span");if(y.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="#D3D3D3">\n <path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/>\n </svg>',y.style.fontSize="0.8em",y.style.cursor="pointer",g.appendChild(f),g.appendChild(y),r.appendChild(g),a.appendChild(r),e.schema_object){const s=t.extractImage(e.schema_object);if(s){const e=document.createElement("div"),t=document.createElement("img"),r=n(s);"#"!==r&&(t.src=r,t.width=80,t.height=80,t.style.objectFit="cover",t.alt="Item image",t.onerror=function(){this.style.display="none"},e.appendChild(t),a.appendChild(e))}}return a}class l{constructor(e=null,t="dropdown",n="list",s="/ask"){e&&(this.site=e),this.apiEndpoint=s;const a=new URLSearchParams(window.location.search),i=a.get("query"),o=a.get("prev");a.get("context_url");const c=a.get("generate_mode");c&&(this.generate_mode=c),this.generate_mode=n,this.display_mode=t,this.prevMessages=o?JSON.parse(decodeURIComponent(o)):[],r(this,t),function(e){e.sendButton.addEventListener("click",(()=>e.sendMessage())),e.input.addEventListener("keydown",(t=>{"Enter"!==t.key||t.shiftKey||(t.preventDefault(),e.sendMessage())}))}(this),this.eventSource=null,this.dotsStillThere=!1,this.resetChatState(),i&&this.sendMessage(decodeURIComponent(i))}resetChatState(){this.messagesArea.innerHTML="",this.messages=[],this.prevMessages=[],this.currentMessage=[],this.currentItems=[],this.itemToRemember=[],this.thisRoundSummary=null}sendMessage(e=null){const t=e||this.input.value.trim();t&&(this.addMessage(t,"user"),this.currentMessage=t,this.input.value="",this.getResponse(t))}addMessage(e,t){const n=document.createElement("div");if(n.className=`message ${t}-message`,"user"==t){this.lastUserMessageDiv=n;const t=document.createElement("span");t.id=function(e){let t=0;for(let n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n),t|=0;return t}(e.toString()),n.appendChild(t),n.appendChild(document.createElement("br")),n.appendChild(document.createElement("br")),this.scrollDiv=t}const s=document.createElement("div");let a;s.className="message-bubble";try{a=JSON.parse(e)}catch(t){a=e}if(Array.isArray(a)){for(;s.firstChild;)s.removeChild(s.firstChild);a.forEach((e=>{const t=this.createJsonItemHtml(e);s.appendChild(t),a.indexOf(e)<a.length-1&&(s.appendChild(document.createElement("br")),s.appendChild(document.createElement("br")))}))}else s.textContent=e;n.appendChild(s),this.messagesArea.appendChild(n),this.messagesArea.scrollTop=this.messagesArea.scrollHeight,this.messages.push({content:e,sender:t}),this.currentMessage=""}extractImage(e){return e&&e.image?this.extractImageInternal(e.image):null}extractImageInternal(e){if("string"==typeof e)return e;if("object"==typeof e&&e.url)return e.url;if("object"==typeof e&&e.contentUrl)return e.contentUrl;if(e instanceof Array){if(e[0]&&"string"==typeof e[0])return e[0];if(e[0]&&"object"==typeof e[0])return this.extractImageInternal(e[0])}return null}createJsonItemHtml(e){return c(e,this)}createIntermediateMessageHtml(e){return function(e){const t=document.createElement("div");return t.className="intermediate-container",t.textContent=e,t}(e)}memoryMessage(e,t){return function(e,t){if(e){const n=document.createElement("div");return n.className="remember-message",n.textContent=e,t.thisRoundRemembered=n,t.bubble.appendChild(n),n}return null}(e,t)}askUserMessage(e,t){!function(e,t){console.log("askUserMessage",e);const n=document.createElement("div");n.className="ask-user-message",n.textContent=e,t.bubble.appendChild(n)}(e,t)}siteIsIrrelevantToQuery(e,t){!function(e,t){console.log("siteIsIrrelevantToQuery",e);const n=document.createElement("div");n.className="site-is-irrelevant-to-query",n.textContent=e,t.bubble.appendChild(n)}(e,t)}itemDetailsMessage(e,t){return function(e,t){if(e){const n=document.createElement("div");return n.className="item-details-message",n.textContent=e,t.thisRoundRemembered=n,t.bubble.appendChild(n),n}return null}(e,t)}resortResults(e){!function(e){if(e.currentItems.length>0){for(e.currentItems.sort(((e,t)=>t[0].score-e[0].score));e.bubble.firstChild;)e.bubble.removeChild(e.bubble.firstChild);e.thisRoundRemembered&&e.bubble.appendChild(e.thisRoundRemembered),e.sourcesMessage&&e.bubble.appendChild(e.sourcesMessage),e.thisRoundSummary&&e.bubble.appendChild(e.thisRoundSummary);for(const[t,n]of e.currentItems)e.bubble.appendChild(n)}}(e)}createInsufficientResultsHtml(){return function(e){const t=document.createElement("div");return t.className="intermediate-container",t.appendChild(document.createElement("br")),e.currentItems.length>0?t.textContent="I couldn't find any more results that are relevant to your query.":t.textContent="I couldn't find any results that are relevant to your query.",t.appendChild(document.createElement("br")),t}(this)}handleFirstMessage(e){!function(e,t){t.dotsStillThere=!1,t.messagesArea.removeChild(t.messagesArea.lastChild)}(0,this)}possiblyAnnotateUserQuery(e,t){!function(e){e.lastUserMessageDiv}(e)}async getResponse(e){this.addMessage("...","assistant"),this.dotsStillThere=!0;try{console.log("generate_mode",this.generate_mode);const n=this.site||(this.siteSelect&&this.siteSelect.value?this.siteSelect.value:""),s=JSON.stringify(this.prevMessages),a=this.generate_mode,r=this.context_url&&this.context_url.value?this.context_url.value:"",i=`query_${(new Date).getTime()}_${Math.floor(1e3*Math.random())}`;console.log("Generated query ID:",i);const o=new URLSearchParams;o.append("query_id",i),o.append("query",e),n&&o.append("site",n),o.append("generate_mode",a),o.append("prev",s),o.append("item_to_remember",this.itemToRemember||""),o.append("context_url",r);const c=o.toString(),l=`${this.apiEndpoint}?${c}`;console.log("url",l),this.eventSource=new t(l),this.eventSource.query_id=i,this.eventSource.connect(this),this.prevMessages.push(e)}catch(e){console.error("Error fetching response:",e)}}createDebugString(){return function(e){const t=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),n=(e,a)=>{const r=" ".repeat(a);if(null===e)return'<span class="null">null</span>';switch(typeof e){case"string":return e.startsWith("http://")||e.startsWith("https://")?`<span class="string url">"${t(e)}"</span>`:`<span class="string">"${t(e)}"</span>`;case"number":return`<span class="number">${e}</span>`;case"boolean":return`<span class="boolean">${e}</span>`;case"object":return Array.isArray(e)?0===e.length?"[]":`[\n${e.map((e=>`${r} ${n(e,a+1)}`)).join(",\n")}\n${r}]`:s(e,a)}},s=(e,s=0)=>{const a=" ".repeat(s);return 0===Object.keys(e).length?"{}":`{\n${Object.entries(e).map((([e,r])=>{const i=e.startsWith("@")?`<span class="keyword">"${t(e)}"</span>`:`<span class="key">"${t(e)}"</span>`;return`${a} ${i}: ${n(r,s+1)}`})).join(",\n")}\n${a}}`};try{const t="string"==typeof e?JSON.parse(e):e;return`<pre class="json-ld"><code>${s(t)}</code></pre>\n<style>\n.json-ld {\n background-color: #f5f5f5;\n padding: 1em;\n border-radius: 4px;\n font-family: monospace;\n line-height: 1.5;\n}\n.json-ld .keyword { color: #e91e63; }\n.json-ld .key { color: #2196f3; }\n.json-ld .string { color: #4caf50; }\n.json-ld .string.url { color: #9c27b0; }\n.json-ld .number { color: #ff5722; }\n.json-ld .boolean { color: #ff9800; }\n.json-ld .null { color: #795548; }\n</style>`}catch(e){return`<pre class="json-ld error">Error: ${e.message}</pre>`}}(this.currentItems)}clearHistory(){this.messagesArea.innerHTML="",this.messages=[],this.prevMessages=[]}}window.ChatInterface=l;let d=null;function h(e="",t="nlwebsearch",n="generate",s=null){return s||(s=window.chatApiEndpoint||"http://localhost:8000/ask"),d||(d=new l(e,t,n,s),d)}function m(){const e=document.getElementById("ai-search-input"),t=document.getElementById("chat-container"),n=e.value.trim();t.style.display="block";const s=h();e.value="",s.sendMessage(n)}window.findChatInterface=h,document.addEventListener("DOMContentLoaded",(()=>{const e=document.getElementById("ai-search-input"),t=document.getElementById("ai-search-button"),n=document.getElementById("close-icon");t&&t.addEventListener("click",m),e&&e.addEventListener("keypress",(e=>{"Enter"===e.key&&m()})),n&&n.addEventListener("click",(()=>{const e=document.getElementById("chat-container");e&&(e.style.display="none")}))})),e.findChatInterface=h})); //# sourceMappingURL=nlweb-js-client.min.js.map