diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetBasicChat.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetBasicChat.kt index 4fc310b0..0a0afa3d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetBasicChat.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetBasicChat.kt @@ -1,17 +1,12 @@ package com.simiacryptus.skyenet.body import com.simiacryptus.openai.OpenAIClient -import org.apache.commons.io.FileUtils -import org.eclipse.jetty.webapp.WebAppContext -import java.io.File open class SkyenetBasicChat( applicationName: String, - baseURL: String, oauthConfig: String? = null ) : SkyenetSessionServerBase( applicationName = applicationName, - baseURL = baseURL, oauthConfig = oauthConfig, ) { override val api: OpenAIClient diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt index 1c725416..bf82d542 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt @@ -8,10 +8,8 @@ import com.simiacryptus.util.describe.TypeDescriber import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import org.apache.commons.io.FileUtils import org.eclipse.jetty.servlet.ServletHolder import org.eclipse.jetty.webapp.WebAppContext -import java.io.File abstract class SkyenetCodingSessionServer( applicationName: String, @@ -21,7 +19,6 @@ abstract class SkyenetCodingSessionServer( resourceBase: String = "simpleSession", val maxRetries: Int = 5, var maxHistoryCharacters: Int = 4000, - baseURL: String = "http://localhost:8080", temperature: Double = 0.1, val model: OpenAIClient.Model = OpenAIClient.Models.GPT4, var useHistory: Boolean = true, @@ -31,7 +28,6 @@ abstract class SkyenetCodingSessionServer( applicationName = applicationName, oauthConfig = oauthConfig, resourceBase = resourceBase, - baseURL = baseURL, temperature = temperature, ) { @@ -40,8 +36,8 @@ abstract class SkyenetCodingSessionServer( abstract fun hands(): java.util.Map abstract fun heart(hands: java.util.Map): Heart - override fun configure(context: WebAppContext, prefix: String) { - super.configure(context, prefix) + override fun configure(context: WebAppContext, prefix: String, baseUrl: String) { + super.configure(context, prefix, baseUrl) context.addServlet(descriptorServlet, "/yamlDescriptor") } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt index 03aecc9c..9ae5218a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt @@ -6,14 +6,11 @@ import com.simiacryptus.util.describe.YamlDescriber import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import org.apache.commons.io.FileUtils import org.eclipse.jetty.servlet.ServletHolder import org.eclipse.jetty.webapp.WebAppContext -import java.io.File open class SkyenetInterviewer( applicationName: String, - baseURL: String, val dataClass: Class, val visiblePrompt: String, val describer: TypeDescriber = YamlDescriber(), @@ -23,14 +20,13 @@ open class SkyenetInterviewer( val apiKey: String ) : SkyenetSessionServerBase( applicationName = applicationName, - baseURL = baseURL, oauthConfig = oauthConfig, ) { override val api: OpenAIClient = OpenAIClient(apiKey) - override fun configure(context: WebAppContext, prefix: String) { - super.configure(context, prefix) + override fun configure(context: WebAppContext, prefix: String, baseUrl: String) { + super.configure(context, prefix, baseUrl) context.addServlet( ServletHolder( "yamlDescriptor", diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt index acc30c13..0cb96a5e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt @@ -7,12 +7,10 @@ import java.util.function.Consumer abstract class SkyenetMacroChat( applicationName: String, - baseURL: String, oauthConfig: String? = null, temperature: Double = 0.1, ) : SkyenetSessionServerBase( applicationName = applicationName, - baseURL = baseURL, oauthConfig = oauthConfig, temperature = temperature, ) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt index d9c240d8..ef617ace 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt @@ -2,15 +2,11 @@ package com.simiacryptus.skyenet.body import com.simiacryptus.openai.OpenAIClient import com.vladsch.flexmark.util.data.MutableDataSet -import org.apache.commons.io.FileUtils -import org.eclipse.jetty.webapp.WebAppContext import java.awt.Desktop -import java.io.File import java.net.URI open class SkyenetMarkupChat( applicationName: String, - baseURL: String, oauthConfig: String? = null, val visiblePrompt: String = """ |Hello! I am here to assist you in a casual conversation! @@ -33,7 +29,6 @@ open class SkyenetMarkupChat( """.trimMargin() ) : SkyenetSessionServerBase( applicationName = applicationName, - baseURL = baseURL, oauthConfig = oauthConfig, ) { override val api: OpenAIClient @@ -67,8 +62,7 @@ open class SkyenetMarkupChat( const val port = 8081 const val baseURL = "http://localhost:$port" var skyenet = SkyenetMarkupChat( - applicationName = "Chat Demo", - baseURL = baseURL + applicationName = "Chat Demo" ) @JvmStatic diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetSessionServerBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetSessionServerBase.kt index 14c596b1..33a32be9 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetSessionServerBase.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetSessionServerBase.kt @@ -16,7 +16,6 @@ abstract class SkyenetSessionServerBase( override val applicationName: String, val oauthConfig: String? = null, resourceBase: String = "simpleSession", - val baseURL: String = "http://localhost:8080", val temperature: Double = 0.1, ) : WebSocketServer(resourceBase) { @@ -27,8 +26,8 @@ abstract class SkyenetSessionServerBase( open val sessionDataStorage = SessionDataStorage(File(File(".skynet"), applicationName)) - override fun configure(context: WebAppContext, prefix: String) { - super.configure(context, prefix) + override fun configure(context: WebAppContext, prefix: String, baseURL: String) { + super.configure(context, prefix, baseURL) if (null != oauthConfig) (AuthenticatedWebsite("$baseURL/oauth2callback", this@SkyenetSessionServerBase.applicationName) { FileUtils.openInputStream(File(oauthConfig)) @@ -181,8 +180,7 @@ abstract class SkyenetSessionServerBase( resp.writer.write( JsonUtil.objectMapper().writeValueAsString( mapOf( - "applicationName" to applicationName, - "baseURL" to baseURL, + "applicationName" to applicationName ) ) ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt index 914f3aac..a05dc93c 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt @@ -86,26 +86,26 @@ abstract class WebSocketServer(val resourceBase: String) { abstract fun newSession(sessionId: String): SessionInterface - fun start(port: Int = 8080): Server { + fun start(port: Int = 8080, baseUrl: String = "http://localhost:$port"): Server { val server = Server(port) - configure(server) + configure(server, baseUrl) server.start() return server } - open fun configure(server: Server) { + open fun configure(server: Server, baseUrl: String) { val webAppContext = WebAppContext() webAppContext.baseResource = baseResource webAppContext.contextPath = "/" webAppContext.welcomeFiles = arrayOf("index.html") JettyWebSocketServletContainerInitializer.configure(webAppContext, null) - configure(webAppContext) + configure(webAppContext, baseUrl = baseUrl) server.handler = webAppContext } open val baseResource: Resource? get() = Resource.newResource(javaClass.classLoader.getResource(resourceBase)) - open fun configure(webAppContext: WebAppContext, prefix: String = "") { + open fun configure(webAppContext: WebAppContext, prefix: String = "", baseUrl: String) { webAppContext.addServlet(ServletHolder(javaClass.simpleName + "/default", defaultServlet), prefix + "/") webAppContext.addServlet(ServletHolder(javaClass.simpleName + "/ws", webSocketHandler), prefix + "/ws/*") webAppContext.addServlet(ServletHolder(javaClass.simpleName + "/newSession", newSessionServlet),prefix + "/newSession") diff --git a/webui/src/main/resources/simpleSession/chat.js b/webui/src/main/resources/simpleSession/chat.js index 6e88b34e..3c38eafd 100644 --- a/webui/src/main/resources/simpleSession/chat.js +++ b/webui/src/main/resources/simpleSession/chat.js @@ -1,3 +1,6 @@ + +let socket; + function getSessionId() { if (!window.location.hash) { fetch('newSession') @@ -17,8 +20,6 @@ function getSessionId() { } } -let socket; - function send(message) { console.log('Sending message:', message); socket.send(message); diff --git a/webui/src/main/resources/simpleSession/index.html b/webui/src/main/resources/simpleSession/index.html index 1b4310fe..6e99a6fb 100644 --- a/webui/src/main/resources/simpleSession/index.html +++ b/webui/src/main/resources/simpleSession/index.html @@ -5,6 +5,7 @@ WebSocket Client + @@ -26,38 +27,6 @@ - -
@@ -66,152 +35,10 @@
- -

Disconnected. Attempting to reconnect...

+ + diff --git a/webui/src/main/resources/simpleSession/main.js b/webui/src/main/resources/simpleSession/main.js new file mode 100644 index 00000000..0e7c063b --- /dev/null +++ b/webui/src/main/resources/simpleSession/main.js @@ -0,0 +1,170 @@ +function showModal(endpoint) { + fetchData(endpoint); + document.getElementById('modal').style.display = 'block'; +} + +function closeModal() { + document.getElementById('modal').style.display = 'none'; +} + +async function fetchData(endpoint) { + try { + const response = await fetch(endpoint); + const text = await response.text(); + document.getElementById('modal-content').innerHTML = "
" + text + "
"; + Prism.highlightAll(); + } catch (error) { + console.error('Error fetching data:', error); + } +} + +function receive(event) { + console.log('WebSocket message:', event); + const messagesDiv = document.getElementById('messages'); + + // Find the first comma and split the received data into ID and message content + const firstCommaIndex = event.data.indexOf(','); + const messageId = event.data.substring(0, firstCommaIndex); + const messageContent = event.data.substring(firstCommaIndex + 1); + + let messageDiv = document.getElementById(messageId); + + if (messageDiv) { + messageDiv.innerHTML = messageContent; + } else { + messageDiv = document.createElement('div'); + messageDiv.className = 'message message-container'; // Add the message-container class + messageDiv.id = messageId; + messageDiv.innerHTML = messageContent; + messagesDiv.appendChild(messageDiv); + } + + messagesDiv.scrollTop = messagesDiv.scrollHeight; + Prism.highlightAll(); + updateMessageDivs(); +} + +function updateMessageDivs() +{ + /* + console.log("Updating message divs"); + Get all message containers + var messageContainers = document.querySelectorAll('.message-container'); + // Loop through each message container + messageContainers.forEach(function (container) { + console.log("Updating message container: " + container); + // Get the first child which is a div (header) and the remaining children (content) + var header = container.children[0]; + while (header.tagName !== 'DIV') { + header = header.nextElementSibling; + } + var content = Array.from(container.children); + // Remove the header from the content array (it isn't nessessary the first element) + content.splice(content.indexOf(header), 1); + + + // Add click event listener to the header + header.addEventListener('click', function () { + console.log("Header clicked: " + header); + // Toggle the collapsible-content class on each content element + content.forEach(function (elem) { + console.log("Toggling collapsible-content class on element: " + elem); + elem.classList.toggle('collapsible-content'); + }); + }); + + // Initialize content as collapsible + content.forEach(function (elem) { + console.log("Adding collapsible-content class to element: " + elem); + elem.classList.add('collapsible-content'); + }); + + // Expand the content initially + header.click(); + }); + */ +} + + +document.addEventListener('DOMContentLoaded', () => { + + document.getElementById('api').addEventListener('click', () => showModal('yamlDescriptor')); + document.getElementById('history').addEventListener('click', () => showModal('sessions')); + document.querySelector('.close').addEventListener('click', closeModal); + + window.addEventListener('click', (event) => { + if (event.target === document.getElementById('modal')) { + closeModal(); + } + }); + + const form = document.getElementById('form'); + const messageInput = document.getElementById('message'); + + form.addEventListener('submit', (event) => { + event.preventDefault(); + send(messageInput.value); + messageInput.value = ''; + }); + + messageInput.addEventListener('keydown', (event) => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + form.dispatchEvent(new Event('submit')); + } + }); + + const sessionId = getSessionId(); + if (sessionId) { + connect(sessionId, receive); + } else { + connect(undefined, receive); + } + + document.body.addEventListener('click', (event) => { + const target = event.target; + if (target.classList.contains('play-button')) { + const messageId = target.getAttribute('data-id'); + send('!' + messageId + ',run'); + } else if (target.classList.contains('regen-button')) { + const messageId = target.getAttribute('data-id'); + send('!' + messageId + ',regen'); + } else if (target.classList.contains('cancel-button')) { + const messageId = target.getAttribute('data-id'); + send('!' + messageId + ',stop'); + } else if (target.classList.contains('href-link')) { + const messageId = target.getAttribute('data-id'); + send('!' + messageId + ',link'); + } else if (target.classList.contains('text-submit-button')) { + const messageId = target.getAttribute('data-id'); + const text = document.querySelector('.reply-input[data-id="' + messageId + '"]').value; + send('!' + messageId + ',userTxt,' + text); + } + }); + + document.getElementById("files").addEventListener("click", function (event) { + event.preventDefault(); // Prevent the default behavior of the anchor tag + const sessionId = getSessionId(); + const url = "fileIndex/" + sessionId + "/"; + window.open(url, "_blank"); // Open the URL in a new tab + }); + + fetch('appInfo') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + if (data.applicationName) { + document.title = data.applicationName; + } + }) + .catch(error => { + console.error('There was a problem with the fetch operation:', error); + }); + + updateMessageDivs(); +}); + diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/BasicChatTest.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/BasicChatTest.kt index 60d1b942..2bfb76a2 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/BasicChatTest.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/BasicChatTest.kt @@ -15,8 +15,7 @@ object BasicChatTest { const val port = 8081 const val baseURL = "http://localhost:$port" var skyenet = SkyenetBasicChat( - applicationName = "Chat Demo", - baseURL = baseURL + applicationName = "Chat Demo" ) @JvmStatic diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt index 532b0b5e..51fb7f8a 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt @@ -17,7 +17,6 @@ class CookbookGenerator( temperature: Double = 0.3 ) : SkyenetMacroChat( applicationName = applicationName, - baseURL = baseURL, temperature = temperature ) { interface CookbookAuthorAPI { diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt index 35d47176..d2b84e6d 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt @@ -3,10 +3,8 @@ package com.simiacryptus.skyenet import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.skyenet.heart.GroovyInterpreter import com.simiacryptus.skyenet.body.SessionServerUtil.asJava import com.simiacryptus.skyenet.body.SkyenetCodingSessionServer -import com.simiacryptus.skyenet.heart.KotlinLocalInterpreter import com.simiacryptus.skyenet.heart.ScalaLocalInterpreter import com.simiacryptus.util.describe.AbbrevWhitelistYamlDescriber diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/FlexmarkChatTest.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/FlexmarkChatTest.kt index 604d4115..ccd1b999 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/FlexmarkChatTest.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/FlexmarkChatTest.kt @@ -15,8 +15,7 @@ object FlexmarkChatTest { const val port = 8081 const val baseURL = "http://localhost:$port" var skyenet = SkyenetMarkupChat( - applicationName = "Chat Demo", - baseURL = baseURL + applicationName = "Chat Demo" ) @JvmStatic diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt index 2d157aaa..49040241 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt @@ -17,7 +17,6 @@ class SkyenetScienceBook( temperature: Double = 0.3 ) : SkyenetMacroChat( applicationName = applicationName, - baseURL = baseURL, temperature = temperature ) { interface ScienceAuthorAPI { diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt index 2363fc2a..cad5e273 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt @@ -16,7 +16,6 @@ class SoftwareProjectGenerator( temperature: Double = 0.3 ) : SkyenetMacroChat( applicationName = applicationName, - baseURL = baseURL, temperature = temperature ) { interface ProjectAPI { diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt index 21171857..584d5a04 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt @@ -16,7 +16,6 @@ class StoryGenerator( temperature: Double = 0.3 ) : SkyenetMacroChat( applicationName = applicationName, - baseURL = baseURL, temperature = temperature ) { interface StoryAPI {