From 7c64ac6d87ce7449e22e3499b77fe78c6664f6d2 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Mon, 21 Oct 2024 22:48:06 +0000 Subject: [PATCH] https://ai.unturf.com/uncloseai.js working & integrated! --- index.html | 155 +++------------------------------------------------ uncloseai.js | 135 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 148 deletions(-) create mode 100644 uncloseai.js diff --git a/index.html b/index.html index 6cbaa63..e3f8663 100644 --- a/index.html +++ b/index.html @@ -18,14 +18,10 @@ max-width: 960px; margin: 0 auto; } - + - - - - + @@ -237,13 +234,12 @@ hermes.ai.unturf.com {

We will likely implement a rate limit based on client IP address.

-

Client Side Only Example, chat with this page.

-

Because we don't use API keys we don't have any real need for a server.

+

Web Client-Only Solution: Interact with AI Services Directly from Static Sites or CDNs

-

view the page source, it's _all_ there!

+

Because we don't require a valid API key, we don't have any real need for a server.

-

TODO: a Javascript CDN bundle of the minimum viable web client for this demo will be hosted and provided free of charge.

+

This web client-only solution uses uncloseai.js which is designed to support static sites or CDNs hosting HTML content. In this architecture, the browser serves as the client, directly interacting with the API without the need for an intermediary server/client. Because we eliminate the requirement for a valid API key, we allow the API to handle requests on behalf of the browser client, making it an efficient & accessible for thin clients which are often on battery.

@@ -255,146 +251,9 @@ hermes.ai.unturf.com {

-

Feel free to message us in the box below.

+

Feel free to message us in the box below.

- - -

Questions, Comments, Discussions

+

Questions & Comments & Discussions

Use the Remarkbox below to tell us what you think! diff --git a/uncloseai.js b/uncloseai.js new file mode 100644 index 0000000..099cd3b --- /dev/null +++ b/uncloseai.js @@ -0,0 +1,135 @@ +import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js"; +import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/highlight.min.js'; + +// Configuration +const API_URL = "https://hermes.ai.unturf.com/v1/chat/completions"; +const API_KEY = "dummy-api-key"; +const MODEL = "NousResearch/Hermes-3-Llama-3.1-8B"; + +// Initialize chat history +let chatHistory = [ + { + role: "system", + content: "You are an AI assistant embedded in a webpage. Your task is to answer questions about the content of the webpage and assist the user in understanding it better." + } +]; + +// Function to extract text content from the webpage +function extractWebpageContent() { + return document.body.innerText; +} + +// Generator function to send a message to the LLM and yield responses +async function* sendMessage(message) { + chatHistory.push({ role: "user", content: message }); + + const response = await fetch(API_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${API_KEY}` + }, + body: JSON.stringify({ + model: MODEL, + messages: chatHistory, + temperature: 0.5, + max_tokens: 5600, + stream: true + }) + }); + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + + for (let i = 0; i < lines.length - 1; i++) { + const line = lines[i].trim(); + if (line.startsWith('data: ')) { + const jsonData = line.slice(6); + if (jsonData === '[DONE]') continue; + + try { + const parsedData = JSON.parse(jsonData); + const content = parsedData.choices[0].delta.content; + if (content) { + yield content; + } + } catch (error) { + console.error('Error parsing JSON:', error); + } + } + } + + buffer = lines[lines.length - 1]; + } +} + +// Function to handle user input and display responses +async function handleUserInput() { + const userInput = document.getElementById('user-input').value; + document.getElementById('user-input').value = ''; + + const chatBox = document.getElementById('chat-box'); + chatBox.innerHTML += `

You: ${userInput}

`; + + // Create a new paragraph for the AI response + const aiResponseParagraph = document.createElement('p'); + aiResponseParagraph.innerHTML = 'AI: '; + chatBox.appendChild(aiResponseParagraph); + + // Create a span element for the actual response content + const responseContent = document.createElement('span'); + aiResponseParagraph.appendChild(responseContent); + + let accumulatedContent = ''; + for await (const chunk of sendMessage(userInput)) { + accumulatedContent += chunk; + const parsedChunk = marked.parse(accumulatedContent); + responseContent.innerHTML = parsedChunk; + + // Apply syntax highlighting to code blocks + responseContent.querySelectorAll('pre code').forEach((block) => { + hljs.highlightElement(block); + }); + } + + chatBox.scrollTop = chatBox.scrollHeight; +} + +// Function to initialize the chat interface +function initializeChatInterface() { + const pageContent = extractWebpageContent(); + chatHistory.push({ + role: "system", + content: `Here's the content of the webpage: ${pageContent}` + }); + + // Set up marked.js options + marked.setOptions({ + highlight: function(code, lang) { + const language = hljs.getLanguage(lang) ? lang : 'plaintext'; + return hljs.highlight(code, { language }).value; + } + }); +} + +document.getElementById("user-input").addEventListener("keydown", function(event) { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + handleUserInput(); + } +}); + +// Initialize the chat interface when the page loads +window.onload = initializeChatInterface; + +// Expose handleUserInput to the global scope +window.handleUserInput = handleUserInput; +