From c8e4beb76f119b9969e8b660ffbcff1f1046b362 Mon Sep 17 00:00:00 2001 From: Andrew Charneski Date: Fri, 10 Nov 2023 04:48:36 -0500 Subject: [PATCH] 1.0.23 --- README.md | 6 +- core/build.gradle.kts | 25 +- .../skyenet/OutputInterceptor.java | 15 +- .../kotlin/com/simiacryptus/skyenet/Brain.kt | 55 ++-- .../kotlin/com/simiacryptus/skyenet/Ears.kt | 2 +- .../kotlin/com/simiacryptus/skyenet/Heart.kt | 2 +- .../com/simiacryptus/skyenet/HeartTestBase.kt | 9 +- .../kotlin/com/simiacryptus/skyenet/Mouth.kt | 136 +++++----- .../simiacryptus/skyenet/actors/BaseActor.kt | 36 +++ .../skyenet/actors/CodingActor.kt | 205 ++++++++++++++ .../skyenet/actors/ParsedActor.kt | 38 +++ .../skyenet/actors/ParsedResponse.kt | 6 + .../skyenet/actors/SimpleActor.kt | 24 ++ .../com/simiacryptus/skyenet/util/AwsUtil.kt | 4 +- .../skyenet/util/LoggingInterceptor.kt | 149 ++++++----- .../skyenet/util}/SessionServerUtil.kt | 135 ++++------ gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 61574 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 12 +- gradlew.bat | 1 + groovy/build.gradle.kts | 6 +- .../skyenet/GroovyInterpreterTest.kt | 10 - java/build.gradle.kts | 6 +- .../skyenet/heart/JavaInterpreter.kt | 165 ------------ kotlin/META-INF/ModuleName.kotlin_module | Bin 0 -> 24 bytes kotlin/build.gradle.kts | 11 +- .../skyenet/heart/KotlinDaemonInterpreter.kt | 40 --- .../skyenet/heart/KotlinInterpreter.kt | 157 +++++++++++ .../skyenet/heart/KotlinLocalInterpreter.kt | 86 ------ .../skyenet/KotlinDaemonInterpreterTest.kt | 11 - ...preterTest.kt => KotlinInterpreterTest.kt} | 34 +-- kotlin/src/test/resources/logback.xml | 14 + scala/build.gradle.kts | 2 +- settings.gradle.kts | 2 +- util/build.gradle.kts | 166 ------------ .../skyenet/util/AgentDemoBase.kt | 45 ---- .../simiacryptus/skyenet/util/SystemTools.kt | 67 ----- webui/build.gradle.kts | 9 +- .../simiacryptus/skyenet/body/ChatSession.kt | 6 +- .../skyenet/body/ClasspathResource.kt | 2 +- .../skyenet/body/MacroChatSession.kt | 6 +- .../skyenet/body/OperationStatus.kt | 2 +- .../skyenet/body/PersistentSessionBase.kt | 2 +- .../skyenet/body/SkyenetCodingSession.kt | 4 +- .../body/SkyenetCodingSessionServer.kt | 2 +- .../skyenet/body/SkyenetInterviewer.kt | 3 +- .../skyenet/body/SkyenetMacroChat.kt | 1 + .../skyenet/body/SkyenetMarkupChat.kt | 2 +- .../skyenet/body/WebSocketServer.kt | 2 +- .../skyenet/servers/AppServerBase.kt | 155 +++++++++++ .../skyenet/servers/CodingActorTestApp.kt | 54 ++++ .../skyenet/servers/CorsFilter.kt | 28 ++ .../skyenet/servers/EmbeddingVisualizer.kt | 67 +++++ .../skyenet/servers/ParsedActorTestApp.kt | 44 +++ .../skyenet/servers/ProxyHttpServlet.kt | 77 ++++++ .../skyenet/servers/ReadOnlyApp.kt | 33 +++ .../skyenet/servers/SessionServerUtil.kt | 40 +++ .../skyenet/servers/SimpleActorTestApp.kt | 37 +++ webui/src/main/resources/welcome/chat.css | 252 ++++++++++++++++++ webui/src/main/resources/welcome/favicon.png | Bin 0 -> 126746 bytes .../skyenet/ActorTestAppServer.kt | 40 +++ .../simiacryptus/skyenet/CookbookGenerator.kt | 180 ------------- .../com/simiacryptus/skyenet/DemoAgent.kt | 2 +- .../skyenet/SkyenetScienceBook.kt | 179 ------------- .../skyenet/SoftwareProjectGenerator.kt | 103 ------- .../simiacryptus/skyenet/StoryGenerator.kt | 168 ------------ .../client_secret_google_oauth.json.kms | 1 + 68 files changed, 1628 insertions(+), 1560 deletions(-) rename {util => core}/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt (98%) rename {util => core}/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt (95%) create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/actors/BaseActor.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/actors/CodingActor.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedActor.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedResponse.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/actors/SimpleActor.kt rename {util => core}/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt (89%) rename {util => core}/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt (97%) rename {webui/src/main/kotlin/com/simiacryptus/skyenet/body => core/src/main/kotlin/com/simiacryptus/skyenet/util}/SessionServerUtil.kt (54%) delete mode 100644 java/src/main/kotlin/com/simiacryptus/skyenet/heart/JavaInterpreter.kt create mode 100644 kotlin/META-INF/ModuleName.kotlin_module delete mode 100644 kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinDaemonInterpreter.kt create mode 100644 kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinInterpreter.kt delete mode 100644 kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinLocalInterpreter.kt delete mode 100644 kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinDaemonInterpreterTest.kt rename kotlin/src/test/kotlin/com/simiacryptus/skyenet/{KotlinLocalInterpreterTest.kt => KotlinInterpreterTest.kt} (53%) create mode 100644 kotlin/src/test/resources/logback.xml delete mode 100644 util/build.gradle.kts delete mode 100644 util/src/main/kotlin/com/simiacryptus/skyenet/util/AgentDemoBase.kt delete mode 100644 util/src/main/kotlin/com/simiacryptus/skyenet/util/SystemTools.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/AppServerBase.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CodingActorTestApp.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CorsFilter.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/EmbeddingVisualizer.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ParsedActorTestApp.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ProxyHttpServlet.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ReadOnlyApp.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SessionServerUtil.kt create mode 100644 webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SimpleActorTestApp.kt create mode 100644 webui/src/main/resources/welcome/chat.css create mode 100644 webui/src/main/resources/welcome/favicon.png create mode 100644 webui/src/test/kotlin/com/simiacryptus/skyenet/ActorTestAppServer.kt delete mode 100644 webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt delete mode 100644 webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt delete mode 100644 webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt delete mode 100644 webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt create mode 100644 webui/src/test/resources/client_secret_google_oauth.json.kms diff --git a/README.md b/README.md index 74e69f58..2588b432 100644 --- a/README.md +++ b/README.md @@ -76,18 +76,18 @@ Maven: com.simiacryptus skyenet-webui - 1.0.22 + 1.0.23 ``` Gradle: ```groovy -implementation group: 'com.simiacryptus', name: 'skyenet', version: '1.0.22' +implementation group: 'com.simiacryptus', name: 'skyenet', version: '1.0.23' ``` ```kotlin -implementation("com.simiacryptus:skyenet:1.0.22") +implementation("com.simiacryptus:skyenet:1.0.23") ``` ### 🌟 To Use diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 12aef6f7..ea0b24a9 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -8,7 +8,7 @@ version = properties("libraryVersion") plugins { java `java-library` - id("org.jetbrains.kotlin.jvm") version "1.7.22" + id("org.jetbrains.kotlin.jvm") version "1.9.20" `maven-publish` id("signing") } @@ -26,16 +26,20 @@ kotlin { jvmToolchain(11) } -val kotlin_version = "1.7.22" +val kotlin_version = "1.9.20" val junit_version = "5.9.2" +val jetty_version = "11.0.17" +val logback_version = "1.2.12" + dependencies { - implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.24") + implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.25") implementation(group = "org.slf4j", name = "slf4j-api", version = "2.0.9") - + implementation(group = "commons-io", name = "commons-io", version = "2.11.0") compileOnlyApi(kotlin("stdlib")) + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3") testImplementation(kotlin("stdlib")) testImplementation(kotlin("script-runtime")) @@ -43,6 +47,19 @@ dependencies { testRuntimeOnly(group = "org.junit.jupiter", name = "junit-jupiter-engine", version = junit_version) compileOnlyApi(group = "org.junit.jupiter", name = "junit-jupiter-api", version = junit_version) compileOnlyApi(group = "org.junit.jupiter", name = "junit-jupiter-engine", version = junit_version) + +// compileOnlyApi(group = "org.eclipse.jetty", name = "jetty-server", version = jetty_version) + compileOnlyApi(group = "com.google.cloud", name = "google-cloud-texttospeech", version = "2.28.0") + compileOnlyApi(group = "com.amazonaws", name = "aws-java-sdk", version = "1.12.454") + compileOnlyApi(group = "ch.qos.logback", name = "logback-classic", version = logback_version) + compileOnlyApi(group = "ch.qos.logback", name = "logback-core", version = logback_version) + +// testImplementation(group = "org.eclipse.jetty", name = "jetty-server", version = jetty_version) + testImplementation(group = "com.google.cloud", name = "google-cloud-texttospeech", version = "2.28.0") + testImplementation(group = "com.amazonaws", name = "aws-java-sdk", version = "1.12.454") + testImplementation(group = "ch.qos.logback", name = "logback-classic", version = logback_version) + testImplementation(group = "ch.qos.logback", name = "logback-core", version = logback_version) + } tasks { diff --git a/core/src/main/java/com/simiacryptus/skyenet/OutputInterceptor.java b/core/src/main/java/com/simiacryptus/skyenet/OutputInterceptor.java index 5e8a6ea9..63db8bc9 100644 --- a/core/src/main/java/com/simiacryptus/skyenet/OutputInterceptor.java +++ b/core/src/main/java/com/simiacryptus/skyenet/OutputInterceptor.java @@ -4,6 +4,7 @@ import java.io.PrintStream; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicBoolean; public class OutputInterceptor { @@ -32,14 +33,14 @@ public static void resetThreadOutputStream() { setOutputStream(centralStream); } - private static final Map threadLocalBuffer = new HashMap<>(); + private static final Map threadLocalBuffer = new WeakHashMap<>(); public static void setOutputStream(ByteArrayOutputStream stream) { threadLocalBuffer.put(Thread.currentThread(), stream); } public static ByteArrayOutputStream getOutputStream() { - return threadLocalBuffer.get(Thread.currentThread()); + return threadLocalBuffer.getOrDefault(Thread.currentThread(), new ByteArrayOutputStream()); } public static String getThreadOutput() { @@ -83,11 +84,13 @@ public void write(byte[] b, int off, int len) { centralStream.reset(); } centralStream.write(b, off, len); - ByteArrayOutputStream stream = getOutputStream(); - if(stream.size() > 1024 * 1024) { - stream.reset(); + ByteArrayOutputStream threadStream = getOutputStream(); + if (threadStream != null) { + if (threadStream.size() > 1024 * 1024) { + threadStream.reset(); + } + threadStream.write(b, off, len); } - stream.write(b, off, len); } }); } diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/Brain.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/Brain.kt index 96441cc4..1d11e722 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/Brain.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/Brain.kt @@ -1,3 +1,5 @@ +@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + package com.simiacryptus.skyenet import com.simiacryptus.openai.OpenAIClient @@ -11,39 +13,28 @@ import java.lang.reflect.Method import java.lang.reflect.Modifier import java.util.concurrent.atomic.AtomicInteger -@Suppress("MemberVisibilityCanBePrivate") open class Brain( val api: OpenAIClient, val symbols: java.util.Map = java.util.HashMap() as java.util.Map, - var model: OpenAIClient.Model = OpenAIClient.Models.GPT35Turbo, - var verbose: Boolean = false, - var temperature: Double = 0.3, - var describer: TypeDescriber = YamlDescriber(), + val model: OpenAIClient.Model = OpenAIClient.Models.GPT35Turbo, + private val verbose: Boolean = false, + val temperature: Double = 0.3, + val describer: TypeDescriber = YamlDescriber(), val language: String = "Kotlin", private val moderated: Boolean = true, - val apiDescription: String = apiDescription(symbols, describer), + private val apiDescription: String = apiDescription(symbols, describer), ) { - val metrics: Map - get() = hashMapOf( - "totalInputLength" to totalInputLength.get(), - "totalOutputLength" to totalOutputLength.get(), - "totalApiDescriptionLength" to totalApiDescriptionLength.get(), - "totalExamplesLength" to totalExamplesLength.get(), - ) + api.metrics - protected val totalInputLength = AtomicInteger(0) - protected val totalOutputLength = AtomicInteger(0) - protected val totalExamplesLength = AtomicInteger(0) - protected val totalApiDescriptionLength: AtomicInteger = AtomicInteger(0) - + private val totalInputLength = AtomicInteger(0) + private val totalOutputLength = AtomicInteger(0) + private val totalApiDescriptionLength: AtomicInteger = AtomicInteger(0) open fun implement(vararg prompt: String): String { if (verbose) log.info("Prompt: \n\t" + prompt.joinToString("\n\t")) - return implement(*getChatMessages(*prompt).toTypedArray()) + return implement(*(getChatSystemMessages(apiDescription) + + prompt.map { ChatMessage(ChatMessage.Role.user, it) }).toTypedArray() + ) } - fun getChatMessages(vararg prompt: String) = getChatSystemMessages(apiDescription) + - prompt.map { ChatMessage(ChatMessage.Role.user, it) } - fun implement( vararg messages: ChatMessage ): String { @@ -143,7 +134,7 @@ open class Brain( if (moderated) api.moderate(json) totalInputLength.addAndGet(json.length) val chatResponse = api.chat(request, model) - var response = chatResponse.choices.first()?.message?.content.orEmpty() + var response = chatResponse.choices.first().message?.content.orEmpty() if (verbose) log.info(response) totalOutputLength.addAndGet(response.length) response = response.trim() @@ -163,7 +154,7 @@ open class Brain( return superMethod?.superMethod() ?: superMethod } - val Class.superclasses: List> + private val Class.superclasses: List> get() { val superclass = superclass val supers = if (superclass == null) listOf() @@ -210,12 +201,6 @@ open class Brain( """.trimMargin() } - /*** - * The input stream is parsed based on ```language\n...\n``` blocks. - * A list of tuples is returned, where the first element is the language and the second is the code block - * For intermediate non-code blocks, the language is "text" - * For unlabeled code blocks, the language is "code" - */ fun extractCodeBlocks(response: String): List> { val codeBlockRegex = Regex("(?s)```(.*?)\\n(.*?)```") val languageRegex = Regex("([a-zA-Z0-9-_]+)") @@ -249,16 +234,6 @@ open class Brain( return result } - fun extractCodeBlock(response: String): String { - var response1 = response - if (response1.contains("```")) { - val startIndex = response1.indexOf('\n', response1.indexOf("```")) - val endIndex = response1.lastIndexOf("```") - val trim = response1.substring(startIndex, endIndex).trim() - response1 = trim - } - return response1 - } } } \ No newline at end of file diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt similarity index 98% rename from util/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt index 2d136e00..9f39be62 100644 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/Ears.kt @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.AtomicInteger /** * The ears are the interface to the audio input for the SkyeNet system */ -@Suppress("MemberVisibilityCanBePrivate") +@Suppress("MemberVisibilityCanBePrivate", "unused") open class Ears( val api: OpenAIClient, val secondsPerAudioPacket : Double = 0.25, diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/Heart.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/Heart.kt index e6a03ed7..ea97d030 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/Heart.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/Heart.kt @@ -4,7 +4,7 @@ interface Heart { fun getLanguage(): String fun run(code: String): Any? - fun validate(code: String): Exception? + fun validate(code: String): Throwable? fun wrapCode(code: String): String = code fun wrapExecution(fn: java.util.function.Supplier): T? = fn.get() diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/HeartTestBase.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/HeartTestBase.kt index b47b6fa7..d7f8d787 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/HeartTestBase.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/HeartTestBase.kt @@ -30,13 +30,14 @@ abstract class HeartTestBase { @Test fun `test validate with invalid code`() { val interpreter = newInterpreter(mapOf() as Map) - assertThrows { interpreter.validate("2 +") } + assertThrows { with(interpreter.validate("2 +")) { throw this!! } } } @Test open fun `test run with variables`() { val interpreter = newInterpreter(mapOf("x" to (2 as Object), "y" to (3 as Object)) as Map) - interpreter.run("x * y") + val result = interpreter.run("x * y") + Assertions.assertEquals(6, result) } @Test @@ -76,13 +77,13 @@ abstract class HeartTestBase { @Test open fun `test validate with tool object and invalid code`() { val interpreter = newInterpreter(mapOf("tool" to (FooBar() as Object)) as Map) - assertThrows { interpreter.validate("tool.baz()") } + assertThrows { with(interpreter.validate("tool.baz()")) { throw this!! } } } @Test open fun `test validate with undefined variable`() { val interpreter = newInterpreter(mapOf() as Map) - assertThrows { interpreter.validate("x * y") } + assertThrows { with(interpreter.validate("x * y")) { throw this!! } } } abstract fun newInterpreter(map: Map): Heart diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt similarity index 95% rename from util/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt index e389c0e9..85cd9a81 100644 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/Mouth.kt @@ -1,69 +1,69 @@ -package com.simiacryptus.skyenet - -import com.google.auth.oauth2.GoogleCredentials -import com.google.cloud.texttospeech.v1.* -import com.google.protobuf.ByteString -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import java.io.FileInputStream -import javax.sound.sampled.AudioFormat -import javax.sound.sampled.AudioSystem -import javax.sound.sampled.DataLine -import javax.sound.sampled.SourceDataLine - -/** - * The mouth is the interface to the Google Text-to-Speech API for the SkyeNet system - */ -@Suppress("MemberVisibilityCanBePrivate") -open class Mouth( - val keyfile: String -) { - - open fun speak(text: String) { - runBlocking { - synthesizeAndPlay("""$text""") - } - } - - protected open val client: TextToSpeechClient by lazy { - val credentials = - GoogleCredentials.fromStream(FileInputStream(keyfile)) - TextToSpeechClient.create(TextToSpeechSettings.newBuilder().setCredentialsProvider { credentials }.build()) - } - - open suspend fun synthesizeAndPlay(ssml: String) { - playAudio(synthesize(ssml).toByteArray()) - } - - open suspend fun synthesize(ssml: String): ByteString { - val input = SynthesisInput.newBuilder().setSsml(ssml).build() - val voice = VoiceSelectionParams.newBuilder() - .setLanguageCode("en-US") - .setSsmlGender(SsmlVoiceGender.FEMALE) - .build() - val audioConfig = AudioConfig.newBuilder() - .setAudioEncoding(AudioEncoding.LINEAR16) - .build() - val audioContent = withContext(Dispatchers.IO) { - client.synthesizeSpeech(input, voice, audioConfig) - }.audioContent - return audioContent - } - - open fun playAudio(audioData: ByteArray) { - val audioFormat = AudioFormat(22050F, 16, 1, true, false) - val info = DataLine.Info(SourceDataLine::class.java, audioFormat) - val sourceDataLine = AudioSystem.getLine(info) as SourceDataLine - try { - sourceDataLine.open(audioFormat) - sourceDataLine.start() - val wavHeaderSize = 44 // The size of a standard WAV header is 44 bytes - sourceDataLine.write(audioData, wavHeaderSize, audioData.size - wavHeaderSize) - sourceDataLine.drain() - sourceDataLine.stop() - } finally { - sourceDataLine.close() - } - } +package com.simiacryptus.skyenet + +import com.google.auth.oauth2.GoogleCredentials +import com.google.cloud.texttospeech.v1.* +import com.google.protobuf.ByteString +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import java.io.FileInputStream +import javax.sound.sampled.AudioFormat +import javax.sound.sampled.AudioSystem +import javax.sound.sampled.DataLine +import javax.sound.sampled.SourceDataLine + +/** + * The mouth is the interface to the Google Text-to-Speech API for the SkyeNet system + */ +@Suppress("MemberVisibilityCanBePrivate", "unused") +open class Mouth( + val keyfile: String +) { + + open fun speak(text: String) { + runBlocking { + synthesizeAndPlay("""$text""") + } + } + + protected open val client: TextToSpeechClient by lazy { + val credentials = + GoogleCredentials.fromStream(FileInputStream(keyfile)) + TextToSpeechClient.create(TextToSpeechSettings.newBuilder().setCredentialsProvider { credentials }.build()) + } + + open suspend fun synthesizeAndPlay(ssml: String) { + playAudio(synthesize(ssml).toByteArray()) + } + + open suspend fun synthesize(ssml: String): ByteString { + val input = SynthesisInput.newBuilder().setSsml(ssml).build() + val voice = VoiceSelectionParams.newBuilder() + .setLanguageCode("en-US") + .setSsmlGender(SsmlVoiceGender.FEMALE) + .build() + val audioConfig = AudioConfig.newBuilder() + .setAudioEncoding(AudioEncoding.LINEAR16) + .build() + val audioContent = withContext(Dispatchers.IO) { + client.synthesizeSpeech(input, voice, audioConfig) + }.audioContent + return audioContent + } + + open fun playAudio(audioData: ByteArray) { + val audioFormat = AudioFormat(22050F, 16, 1, true, false) + val info = DataLine.Info(SourceDataLine::class.java, audioFormat) + val sourceDataLine = AudioSystem.getLine(info) as SourceDataLine + try { + sourceDataLine.open(audioFormat) + sourceDataLine.start() + val wavHeaderSize = 44 // The size of a standard WAV header is 44 bytes + sourceDataLine.write(audioData, wavHeaderSize, audioData.size - wavHeaderSize) + sourceDataLine.drain() + sourceDataLine.stop() + } finally { + sourceDataLine.close() + } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/actors/BaseActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/BaseActor.kt new file mode 100644 index 00000000..b7525d3a --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/BaseActor.kt @@ -0,0 +1,36 @@ +package com.simiacryptus.skyenet.actors + +import com.simiacryptus.openai.OpenAIClient + +abstract class BaseActor( + open val prompt: String, + val name: String? = null, + val api: OpenAIClient = OpenAIClient(), + val model: OpenAIClient.Models = OpenAIClient.Models.GPT35Turbo, + val temperature: Double = 0.3, +) { + + open fun response(vararg messages: OpenAIClient.ChatMessage, model: OpenAIClient.Models = this.model) = api.chat( + OpenAIClient.ChatRequest( + messages = messages.toList().toTypedArray(), + temperature = temperature, + model = this.model.modelName, + ), + model = this.model + ) + abstract fun answer(vararg messages: OpenAIClient.ChatMessage): T + open fun answer(vararg questions: String): T = answer(*chatMessages(*questions)) + + open fun chatMessages(vararg questions: String) = arrayOf( + OpenAIClient.ChatMessage( + role = OpenAIClient.ChatMessage.Role.system, + content = prompt + ), + ) + questions.map { + OpenAIClient.ChatMessage( + role = OpenAIClient.ChatMessage.Role.user, + content = it + ) + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/actors/CodingActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/CodingActor.kt new file mode 100644 index 00000000..e301acd5 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/CodingActor.kt @@ -0,0 +1,205 @@ +package com.simiacryptus.skyenet.actors + +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.skyenet.Brain +import com.simiacryptus.skyenet.Brain.Companion.indent +import com.simiacryptus.skyenet.Brain.Companion.superMethod +import com.simiacryptus.skyenet.Heart +import com.simiacryptus.skyenet.OutputInterceptor +import com.simiacryptus.skyenet.util.SessionServerUtil.asJava +import com.simiacryptus.skyenet.util.SessionServerUtil.getCode +import com.simiacryptus.skyenet.util.SessionServerUtil.getRenderedResponse +import com.simiacryptus.util.describe.AbbrevWhitelistYamlDescriber +import java.lang.reflect.Modifier +import kotlin.reflect.KClass + +class CodingActor( + private val interpreterClass: KClass, + private val symbols: Map = mapOf(), + private val describer: AbbrevWhitelistYamlDescriber = AbbrevWhitelistYamlDescriber( + "com.simiacryptus", + "com.github.simiacryptus" + ), + name: String? = interpreterClass.simpleName, + val details: String? = null, + api: OpenAIClient = OpenAIClient(), + model: OpenAIClient.Models = OpenAIClient.Models.GPT35Turbo, + val fallbackModel: OpenAIClient.Models = OpenAIClient.Models.GPT4Turbo, + temperature: Double = 0.1, +) : BaseActor( + prompt = "", + name = name, + api = api, + model = model, + temperature = temperature, +) { + val fixIterations = 3 + val fixRetries = 2 + + override val prompt: String + get() { + val types = ArrayList>() + val apiobjs = symbols.map { (name, utilityObj) -> + val clazz = Class.forName(utilityObj.javaClass.typeName) + val methods = clazz.methods + .filter { Modifier.isPublic(it.modifiers) } + .filter { it.declaringClass == clazz } + .filter { !it.isSynthetic } + .map { it.superMethod() ?: it } + .filter { it.declaringClass != Object::class.java } + types.addAll(methods.flatMap { (listOf(it.returnType) + it.parameters.map { it.type }).filter { it != clazz } }) + types.addAll(clazz.declaredClasses.filter { Modifier.isPublic(it.modifiers) }) + """ + |$name: + | operations: + | ${Brain.joinYamlList(methods.map { describer.describe(it) }).indent().indent()} + |""".trimMargin().trim() + }.toTypedArray() + val typeDescriptions = types + .filter { !it.isPrimitive } + .filter { !it.isSynthetic } + .filter { !it.name.startsWith("java.") } + .filter { !setOf("void").contains(it.name) } + .distinct().map { + """ + |${it.simpleName}: + | ${describer.describe(it).indent()} + """.trimMargin().trim() + }.toTypedArray() + val apiDescription = """ + |api_objects: + | ${apiobjs.joinToString("\n").indent()} + |components: + | schemas: + | ${typeDescriptions.joinToString("\n").indent().indent()} + """.trimMargin() + return """ + |You will translate natural language instructions into + |an implementation using ${interpreter.getLanguage()} and the script context. + |Use ``` code blocks labeled with ${interpreter.getLanguage()} where appropriate. + |Defined symbols include ${symbols.keys.joinToString(", ")}. + |The runtime context is described below: + | + |$apiDescription + | + |${details ?: ""} + |""".trimMargin().trim() + + } + + open val interpreter by lazy { interpreterClass.java.getConstructor(Map::class.java).newInstance(symbols) } + + override fun answer(vararg questions: String): CodeResult = answer(*chatMessages(*questions)) + + override fun answer(vararg messages: OpenAIClient.ChatMessage): CodeResult { + return CodeResultImpl(*messages) + } + + private inner class CodeResultImpl( + vararg messages: OpenAIClient.ChatMessage + ) : CodeResult { + var _status = CodeResult.Status.Coding + override fun getStatus(): CodeResult.Status { + return _status + } + + val impl by lazy { + var codedInstruction = implement( + Brain( + api = api, + symbols = symbols.mapValues { it as Object }.asJava, + language = interpreter.getLanguage(), + describer = describer, + model = model, + temperature = temperature, + ), *messages + ) + if (_status != CodeResult.Status.Success) { + codedInstruction = implement( + Brain( + api = api, + symbols = symbols.mapValues { it as Object }.asJava, + language = interpreter.getLanguage(), + describer = describer, + model = fallbackModel, + temperature = temperature, + ), *messages + ) + } + if (_status != CodeResult.Status.Success) { + log.info("Failed to implement ${messages.map { it.content }.joinToString("\n")}") + _status = CodeResult.Status.Failure + } + codedInstruction + } + + private fun implement( + brain: Brain, + vararg messages: OpenAIClient.ChatMessage + ): String { + val response = brain.implement(*messages) + val codeBlocks = Brain.extractCodeBlocks(response) + var codedInstruction = "" + for (codingAttempt in 0..fixRetries) { + var renderedResponse = getRenderedResponse(codeBlocks) + codedInstruction = getCode(interpreter.getLanguage(), codeBlocks) + log.info("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin()) + log.info("Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin()) + for (fixAttempt in 0..fixIterations) { + try { + val validate = interpreter.validate(codedInstruction) + if (validate != null) throw validate + log.info("Validation succeeded") + _status = CodeResult.Status.Success + return codedInstruction + } catch (ex: Throwable) { + log.info("Validation failed - ${ex.message}") + _status = CodeResult.Status.Correcting + val respondWithCode = brain.fixCommand(codedInstruction, ex, "", *messages) + renderedResponse = getRenderedResponse(respondWithCode.second) + codedInstruction = getCode(interpreter.getLanguage(), respondWithCode.second) + log.info("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin()) + log.info("Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin()) + } + } + } + return codedInstruction + } + + override fun getCode(): String { + return impl + } + + override fun run(): ExecutionResult { + //language=HTML + log.info("Running ${getCode()}") + OutputInterceptor.clearGlobalOutput() + val result = interpreter.run(getCode()) + log.info("Result: $result") + //language=HTML + val executionResult = ExecutionResult(result.toString(), OutputInterceptor.getGlobalOutput()) + OutputInterceptor.clearGlobalOutput() + return executionResult + } + } + + companion object { + val log = org.slf4j.LoggerFactory.getLogger(CodingActor::class.java) + + } +} + +data class ExecutionResult( + val resultValue: String, + val resultOutput: String +) + +interface CodeResult { + enum class Status { + Coding, Correcting, Success, Failure + } + + fun getStatus(): Status + fun getCode(): String + fun run(): ExecutionResult +} diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedActor.kt new file mode 100644 index 00000000..66d795b1 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedActor.kt @@ -0,0 +1,38 @@ +package com.simiacryptus.skyenet.actors + +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.openai.proxy.ChatProxy +import java.util.function.Function + +class ParsedActor( + val parserClass: Class>, + prompt: String, + val action: String? = null, + api: OpenAIClient = OpenAIClient(), + model: OpenAIClient.Models = OpenAIClient.Models.GPT35Turbo, + temperature: Double = 0.3, +) : BaseActor>( + api = api, + prompt = prompt, + name = action, + model = model, + temperature = temperature, +) { + private val parser: Function = ChatProxy( + clazz = parserClass, + api = api, + model = OpenAIClient.Models.GPT35Turbo, + temperature = temperature, + ).create() + private inner class ParsedResponseImpl(vararg messages: OpenAIClient.ChatMessage) : ParsedResponse { + private val _text: String by lazy { response(*messages).choices.first().message?.content ?: throw RuntimeException("No response") } + private val _obj: T by lazy { parser.apply(getText()) } + override fun getText(): String = _text + override fun getObj(): T = _obj + } + + override fun answer(vararg messages: OpenAIClient.ChatMessage): ParsedResponse { + return ParsedResponseImpl(*messages) + } +} + diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedResponse.kt new file mode 100644 index 00000000..a447dab3 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/ParsedResponse.kt @@ -0,0 +1,6 @@ +package com.simiacryptus.skyenet.actors + +interface ParsedResponse { + fun getText(): String + fun getObj(): T +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/actors/SimpleActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/SimpleActor.kt new file mode 100644 index 00000000..0415dc18 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/actors/SimpleActor.kt @@ -0,0 +1,24 @@ +package com.simiacryptus.skyenet.actors + +import com.simiacryptus.openai.OpenAIClient + +class SimpleActor( + prompt: String, + name: String? = null, + api: OpenAIClient = OpenAIClient(), + model: OpenAIClient.Models = OpenAIClient.Models.GPT35Turbo, + temperature: Double = 0.3, +) : BaseActor( + prompt = prompt, + name = name, + api = api, + model = model, + temperature = temperature, + +) { + + override fun answer(vararg questions: String): String = answer(*chatMessages(*questions)) + + override fun answer(vararg messages: OpenAIClient.ChatMessage): String = response(*messages).choices.first().message?.content ?: throw RuntimeException("No response") +} + diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt similarity index 89% rename from util/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt index 9671f3a2..1e068cdc 100644 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/util/AwsUtil.kt @@ -25,8 +25,8 @@ object AwsUtil { Files.write(outputPath, encryptedData.toByteArray()) } - fun decryptResource(resourceFile: String): String { - val encryptedData = javaClass.classLoader.getResourceAsStream(resourceFile)?.readAllBytes() + fun decryptResource(resourceFile: String, classLoader: ClassLoader = javaClass.classLoader!!): String { + val encryptedData = classLoader.getResourceAsStream(resourceFile)?.readAllBytes() if (null == encryptedData) { throw RuntimeException("Unable to load resource: $resourceFile") } diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt similarity index 97% rename from util/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt index 56d4faaa..f7bd634e 100644 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/util/LoggingInterceptor.kt @@ -1,74 +1,75 @@ -package com.simiacryptus.skyenet.util - -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.LoggerContext -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.AppenderBase -import org.slf4j.LoggerFactory - -class LoggingInterceptor( - private val stringBuffer: StringBuffer = StringBuffer(), -) : AppenderBase() { - companion object { - fun withIntercept( - stringBuffer: StringBuffer = StringBuffer(), - vararg loggerPrefixes: String, - fn: () -> T, - ): T { - val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext - val loggers = loggerPrefixes.flatMap { loggerPrefix -> - loggerContext.loggerList.filter { it.name.startsWith(loggerPrefix) } - } - return withIntercept( - stringBuffer = stringBuffer, - loggers = loggers.toTypedArray(), - fn = fn - ) - } - - private fun withIntercept( - stringBuffer: StringBuffer, - vararg loggers: Logger, - fn: () -> T, - ): T { - // Save the original logger level and appender list - val originalLevels = loggers.map { it.level } - val originalAppenders = loggers.map { it.iteratorForAppenders().asSequence().toList() } - - // Create and attach the custom StringBufferAppender - val stringBufferAppender = LoggingInterceptor(stringBuffer) - stringBufferAppender.context = LoggerFactory.getILoggerFactory() as LoggerContext - stringBufferAppender.start() - loggers.forEach { it.detachAndStopAllAppenders() } - loggers.forEach { it.addAppender(stringBufferAppender) } - - try { - return fn() - } finally { - // Restore the original logger level and appender list - loggers.zip(originalLevels.zip(originalAppenders)).forEach { (jsEngineLogger, t) -> - val (originalLevel, originalAppender) = t - jsEngineLogger.level = originalLevel - jsEngineLogger.detachAndStopAllAppenders() - originalAppender.forEach { jsEngineLogger.addAppender(it) } - } - } - } - } - - override fun addInfo(msg: String?, ex: Throwable?) { - super.addInfo(msg, ex) - } - - override fun append(event: ILoggingEvent) { - stringBuffer.append(event.formattedMessage) - event.throwableProxy?.let { - stringBuffer.append(System.lineSeparator()) - stringBuffer.append(ch.qos.logback.classic.pattern.ThrowableProxyConverter().convert(event)) - } - stringBuffer.append(System.lineSeparator()) - } - - fun getStringBuffer(): StringBuffer = stringBuffer - -} +package com.simiacryptus.skyenet.util + +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.LoggerContext +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import org.slf4j.LoggerFactory + +@Suppress("unused") +class LoggingInterceptor( + private val stringBuffer: StringBuffer = StringBuffer(), +) : AppenderBase() { + companion object { + fun withIntercept( + stringBuffer: StringBuffer = StringBuffer(), + vararg loggerPrefixes: String, + fn: () -> T, + ): T { + val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext + val loggers = loggerPrefixes.flatMap { loggerPrefix -> + loggerContext.loggerList.filter { it.name.startsWith(loggerPrefix) } + } + return withIntercept( + stringBuffer = stringBuffer, + loggers = loggers.toTypedArray(), + fn = fn + ) + } + + private fun withIntercept( + stringBuffer: StringBuffer, + vararg loggers: Logger, + fn: () -> T, + ): T { + // Save the original logger level and appender list + val originalLevels = loggers.map { it.level } + val originalAppenders = loggers.map { it.iteratorForAppenders().asSequence().toList() } + + // Create and attach the custom StringBufferAppender + val stringBufferAppender = LoggingInterceptor(stringBuffer) + stringBufferAppender.context = LoggerFactory.getILoggerFactory() as LoggerContext + stringBufferAppender.start() + loggers.forEach { it.detachAndStopAllAppenders() } + loggers.forEach { it.addAppender(stringBufferAppender) } + + try { + return fn() + } finally { + // Restore the original logger level and appender list + loggers.zip(originalLevels.zip(originalAppenders)).forEach { (jsEngineLogger, t) -> + val (originalLevel, originalAppender) = t + jsEngineLogger.level = originalLevel + jsEngineLogger.detachAndStopAllAppenders() + originalAppender.forEach { jsEngineLogger.addAppender(it) } + } + } + } + } + + override fun addInfo(msg: String?, ex: Throwable?) { + super.addInfo(msg, ex) + } + + override fun append(event: ILoggingEvent) { + stringBuffer.append(event.formattedMessage) + event.throwableProxy?.let { + stringBuffer.append(System.lineSeparator()) + stringBuffer.append(ch.qos.logback.classic.pattern.ThrowableProxyConverter().convert(event)) + } + stringBuffer.append(System.lineSeparator()) + } + + fun getStringBuffer(): StringBuffer = stringBuffer + +} diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SessionServerUtil.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/util/SessionServerUtil.kt similarity index 54% rename from webui/src/main/kotlin/com/simiacryptus/skyenet/body/SessionServerUtil.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/util/SessionServerUtil.kt index 625dbc61..3a0e82b1 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SessionServerUtil.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/util/SessionServerUtil.kt @@ -1,81 +1,56 @@ -package com.simiacryptus.skyenet.body - -import com.simiacryptus.openai.OpenAIClient.ChatMessage - -object SessionServerUtil { - fun getRenderedResponse(respondWithCode: List>) = - respondWithCode.joinToString("\n") { - var language = it.first - if (language == "code") language = "groovy" - if (language == "text") { - //language=HTML - """ - |
- |${it.second} - |
- |""".trimMargin().trim() - } else { - //language=HTML - """ - |

-                    |${it.second}
-                    |
- |""".trimMargin().trim() - } - } - - val logger = org.slf4j.LoggerFactory.getLogger(SkyenetCodingSessionServer::class.java) - - fun getCode(language: String, textSegments: List>) = - textSegments.joinToString("\n") { - if (it.first.lowercase() == "code" || it.first.lowercase() == language.lowercase()) { - logger.debug("Selected: $language: ${it.second}") - """ - |${it.second} - |""".trimMargin().trim() - } else { - logger.debug("Not Selected: ${it.first}: ${it.second}") - "" - } - } - - operator fun java.util.Map.plus(mapOf: Map): java.util.Map { - val hashMap = java.util.HashMap() - this.forEach(hashMap::put) - hashMap.putAll(mapOf) - return hashMap as java.util.Map - } - - val Map.asJava: java.util.Map - get() { - return java.util.HashMap().also { map -> - this.forEach { (key, value) -> - map[key] = value - } - } as java.util.Map - } - - fun getHistory(statuses: Collection, maxEntries: Int, maxChars: Int): List { - val messages: java.util.ArrayList = ArrayList() - val operationStatusList = statuses - .filter { it.status != OperationStatus.OperationState.Pending } - .filter { it.status != OperationStatus.OperationState.Running } - .filter { it.status != OperationStatus.OperationState.Implemented } - .filter { !it.instruction.startsWith("!!!") } - .sortedBy { it.created } - .takeLast(maxEntries) - for (operationStatus in operationStatusList) { - messages.addAll(operationStatus.chatMessages) - } - if (messages.isEmpty()) return messages - var totalCharacters = 0 - val truncatedMessages = mutableListOf() - for (message in messages.reversed()) { - totalCharacters += message.content?.length ?: 0 - if (totalCharacters > maxChars) break - truncatedMessages.add(message) - } - return truncatedMessages.reversed() - } - +package com.simiacryptus.skyenet.util + +import com.simiacryptus.openai.OpenAIClient.ChatMessage + +object SessionServerUtil { + fun getRenderedResponse(respondWithCode: List>) = + respondWithCode.joinToString("\n") { + var language = it.first + if (language == "code") language = "groovy" + if (language == "text") { + //language=HTML + """ + |
+ |${it.second} + |
+ |""".trimMargin().trim() + } else { + //language=HTML + """ + |

+                    |${it.second}
+                    |
+ |""".trimMargin().trim() + } + } + + val logger = org.slf4j.LoggerFactory.getLogger(SessionServerUtil::class.java) + + fun getCode(language: String, textSegments: List>) = + textSegments.joinToString("\n") { + if (it.first.lowercase() == "code" || it.first.lowercase() == language.lowercase()) { + """ + |${it.second} + |""".trimMargin().trim() + } else { + "" + } + } + + operator fun java.util.Map.plus(mapOf: Map): java.util.Map { + val hashMap = java.util.HashMap() + this.forEach(hashMap::put) + hashMap.putAll(mapOf) + return hashMap as java.util.Map + } + + val Map.asJava: java.util.Map + get() { + return java.util.HashMap().also { map -> + this.forEach { (key, value) -> + map[key] = value + } + } as java.util.Map + } + } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 1be5e1bc..0199a4cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Gradle Releases -> https://github.com/gradle/gradle/releases libraryGroup = com.simiacryptus.skyenet -libraryVersion = 1.0.22 +libraryVersion = 1.0.23 gradleVersion = 7.6.1 # Opt-out flag for bundling Kotlin standard library -> https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832f090a2944b7473328c07c9755baa3196..943f0cbfa754578e88a3dae77fce6e3dea56edbf 100644 GIT binary patch delta 36524 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%0DGK?7GHTj zQvM5soNUybecue#S5)q-U*Q?+5f8Y)E2RhP-d<;d%}&V27sTGyiLYMIM_Ih#lyo*G8-5Tx!Q7JQc&3id{kCsLB(^v-K>GYyTAh6-=qBd9_d;JZ> zf|;n9nCRSF-K@|Igh^RhKzyTmRfs!n(k~K%ND*t3YMS8BZm`-tNGyn;8y9eXYW!$3 zMqZPmvu~L%04^w9_lELDnm!!7{bRXy6mDjEY|V)+ZM&FI`{|I19X)vuda{{RWW{;u z)z$P=YlmS3&RI9);fj05mWjaGhjL{;JR~GT$G3DRSn5}=(gp7HEHqY# zUco3+)h4Z)IGp-hwoX*X7&WlPM#D_;p-Qswh{4%|nePeLof2(nfGsRpS@+jFDH~EH zKqfw?rT2RmbS5(RG(G2ewd8ug-byd%ec$cK17+N-U+=r}Lss6T1j>t(yFEC2vw2Iw z_6Ni#xo4LoD-fL1I~t!=9V^+f9}+IJu5enLUsz{PpDb(O6&l0@dJ2@1Kt9QW@J-{v zfJ+S}3LwCUT&l7%`BDvy^JvapD zziav5dg)nrpE`uWB6jd`6s<(S(66{zrF~Ap@p)5d-_=;V0v58xzu-S^X$nr+&V?D) zrR*dloi#@4=zqp6e!9&MM81h=aa6S51#7|hzeg<};xhTy+7Tt*a=$F?L`3lPE z5H1EvfO`Cmu-Y(5j{>RS&4gCgYomh#AQ?AxwrA{VM=5(SdRmGQ^{@XdSD81*w>!Ao zE^Iu#f9$gk8367-I&tF11y18ZLNXl87dg^F33_)NFZ86ZA1}T`Sgeh4zuZK0>;FEvO*+*?-w{r=VKv zy7I4~fa>CoovB-6hvrWs{@hNE>#m*8_rJc^mup|V4?p}|UPefo`uBPiQ&|kcp#H2B)??6YgN!qdayMyd(4{)tV2>`Tya0;=&-t@O8~@_9dy#jKm0ZU&?FpfQpZ56ReK>*O==^LBb3jF>gc#o7LY<_t-5SNGmbo;#^< z0hOu}01(w}@f87R7!)t5SyWgst|&oS#Nof0i7M1+($=*nr7*CZm4);ytB1u;_bn7)KJ5|?g(C%K>6`(zmZ?%^{mh2B?bZO%s^QyQxX+2dmPhU)yY0WbPh@r!f=_dzI7$TRK=V)q~n=*Jbhb1Z;Z^k}pL; zKq3kOk(E;kC3zM~D=V%nM{Y^chcv==$Jj}_i}rEcmIc@uiubpmdqeG@Q`yOvH5cxB zz3^ivLx7ys7zPW(-H1R47}XFSP@?!&?3%r_1vtF~2k7rJLBt-Y!}?CW0fAVCK#4L7 zYv>vbfaWm4FCCE6Ye)Ve-*ydPG*7GdYk?XF8T#5@o`qrrGLmFj_(1N!tfB;7_4`@D*F!R7SYcyAU~V9b#XjE=5$ z#UzF>JWxE1bTbD z-*lGJM!zNQiL&BcMOAj91x@fRywj@hG2 zmB&N?8>X<41q^;r5qK?p|9!(x$$W6Af=xxL^h)Wn+^$-(?#icC?yce9!H7Za`z=b# z)fc%;dBskfHbX`X8gRWpcALR5nA>SUKNV^SdM292pk1e}FpZV4O zctIFCXlNo*(R!)pj?LUeLmAyYar<8S6oXODyF2uG+i*)K`xoy9Qn)ydQexLS^0|%g zLUse>W-lZw{h(j|{AGuV+ryjGUoWa_DGp3M+_jWU#{LxVL48?ZVuHrp1S0eAwOJEw z1l~EZrezdtl~J=4J!^!wguA+YE&H@~S-w8E4beMNS;c-SlHmRFq%0zdTM0)z&qCv9 z_Su$b53XnfD{{7um;S{+(3PN+@U|^rC{0 zryteC4KEJZAmTjm;Ej{IKp-W^;rZ=3l5H+9AQ#+O+|#=yKkG4R%nS*y3P3WkpyLMf zu!lw8mX<1P@MJ=;pi3`sW4wHuZ#4$R#how95rngW-hTL=B7ZQSGi*VZDHvCBM5$m1 zF_l`3O!AftmNR?)PV^c(aJ?aH^~I|8Sd-Jc+DTD0ojwa3Bfhc}46-uJ#Hr~Efy-Iw zNQqi3x`(RQzr=m9<{XKPUQ2a&5?S4{E;qH6&S03+A|~e!vw@q zZh0_Cp@#rq?^l=W#fom)@r25FtwLk>=LBI4Pd1aPoU4nkj}}^U?&^Jeb+dQ_5duG4 z*3fLz{E?tUb;wRfI(LQ^w^}2HT^CVowPAj51#S5D&+`jk{K%&g=Q%j-W9nbZ4yre;4{s(izp^_8u3ncj-&05|+T-Qp7?0}(k3(Z$P zV<^h|O_w)Z=~f{s{QifoEMb7`x>|h5R?seL&;y@}u5ZGYU)KXVk<`1?4u3yeK6l`! z)-5OGnTmnVrp)i(x$d#yUiNURMTiRFmYWe^WJh>7x?@MJ(XD6&&(q(3lBuj)_$s7r~F>yb<2`0!y$wYI-N6LbZfxQ%fR90m+Y)T>EyXtRccO$(u;y)?G zWg!cz?hVF|Gz3D!fmv8M5;~svg;%_g1ALLnL7u0T8Bbb!pO1640*7DU{@b6PJ5oCL z`WFqu{zoOC|9>h$B26h9U=6oy_W@EYOS(tP1zGHc5t_dX|k?eqS5gb{?CmmNt$KBO2txD$SYnf{b& z+~J?uOpad(FFtkPRpY+Ki2+|;E%G-JX49;f}=MDE2}}s>+49uOIu{@ zX`v!P%kfk;x|pJjS*tzL(eE|krh8Oj=+rXKCvm(d_StHq^{m}22Q%Q=+%w=%F_O#e zQu-QY=nKMJR8Er)*bs24IAp2ybozReiLTcesMW>cex`M z6@z6I7vtlgCMELB!W3I0;7oxWQ10{4JtMrC6}QVWF?L%^KX1yJlj&U2>L2i@GQrQolHhqp* z6Wce)ZKPo^(z@jLX@C~SeMJ1Pmk9~dzU9ZdoVZ&~2WY`~>!>aXP_m?RczA5hmz>Q8 zf6HLETIh2A8DWtzpTtTphq*9*m(WQD);O5XVFOB|7_X~@9Pfi%O+o{a(F9Hv)&P4I zLA4uz3%VbYH{|{0v@>a(&^f=nv!d^L?d8VxO!w8;naO*<14T$&5d2Xik9mV;5mB5@ zBNxuP0Km?I7jen!m0qY!v#{oz5&yj{kFE5mne~+S9q0GmaxRO|` z$sku2_ua8NSKZt@Lbi7CjMTdV-nVzgWxjU44aiY{Zxb?IhJG#`>;KK2Y+snWA_cS$ z%W=~mJmPR%G~taH+6S`Y7ITT5S|?P~`)<>bYO`)v+_DP*voqDqb-Jahogx{CXAda3 z<+qwRx%9Cor_S7&+|>u{(Hk!7M2jm9p}F)PXGs)A4yp3mt=b25(Q&UFxd$W#C@sbH4~!y6E2<-)^qezJl?^>>XzQ!xHscWi#=mg@adE8sVxNK{Lpu4^}x1GZ91rp#(>t=Brs9hOq2qH!~3wl!Kj=#`Zg z+K%NLDU62OEw%oLaxSY*u-5Q1JQzKxu_QEnc(WxkqFkRhpvW#{?uXZ8)C8>|*IT-h zPv#KNDlHUI)GzEH@1RExPJJ)Yw1vY}FFiR*B3QVp0gIe#4pZcxvl$rPWLtI40+u!i zq{s(&s@e9!R9Cib$rCT8(#qW{9SUddR}qL#w2@oA=t5vQY`)}5cXVbE!4B1bpLKtrBWKasWkkb>ukCNS0V7NwsdXoRD*a=bgYCz)8R zn+)Oh_G*>b&X?I8Jdd}LiWY!qG-%*M_xE(d;;*+ROLpYAHmsY7?p4#S02-AI(p!F^ zCzfuU54mGCU#dVIi|vuI;Dbt4@+CuW_^@60%L_WWv`$E`=N+A)VWF8R*hD=RS!Wri zE8R9X^K0xh$(4Y{xp5j~u!mHtMxZh|N7^*!wru}V;#_#ai594yBZw9lV09@?hIV^8 zvb0y`{cfDiFMVDw+_6s{4J@p+)x*#w9R?WwPPSGE^1{RQ;^~Kxeppj zkSDi)`5>LeDMSDvw^&2y>dm2t-83gJ*fajg3&PKtfdf8;N+&-N!;{y*&8}%0iYlAv z`cKn0yRC@PLsbx!+fak+La69{Ytk8pYO+&u-k+ z%x(qzE@TQJMJ*?w0{GmF@T_Vxu zShGX8L*T0oCfH}%&mm%1jwMMm?xNWJeXxMG!k;pqSRX^X&`!&ziICf%BVW#E zN_N=(%P?ax;B|zK!S#ZkMx@Axt;;rtj^&igb30F9&I*!GIu`rE>MdGGVKx!cCxC(N z^uRe>2&`!*ukz)d^Chi9Z_T+&NPRXLQdd0H>H{Ls4%o#-=nl7Ae!=i)TiV@taSgoQ z-B1ebMqI~)uIEAcOR@uj>_{#eXRfKO9^F5-%XpiLOzmjql!b*xM0>qgi}j(}y|G(+ zdxFp%+7sh3U>noVy1NnSE1&KIID|?bv@`7-jg45SlJl571 z)0zxF4D7oiq1W1k{1ReW4mE)(I%ys3_2>(6uKB)xYe2~?G%dUm{=8Y}rP!$7zW{)SaWc@brYM+LuuJn_wlShyIMFH=dU?=Xw z8dWP-o`xTzwZ<);bw#a$J}}q95dY)f=Nk8ewae&+<)f-^C%N>*K+sduTi6b6WZst! zJVyfEp%vB|yq!fK{q=Hdj#HXqrh!}r9{5Y(jiAzPcZ2v63i%}oBCyoOYz*5PgP33zGw zs2J{Hd3pYT3j7)c`X3ldyIEh@{x9CD-T*yD+-mP?U+2o&)bhJ{*4=qw!-R&+TjnvS+{zEIL#HRMsiBfk5~* zI~}7`ysPbIRp6YZS)F1+E7{`h9q^Vs*(YzQn#^x%<3Zjz@)nOF)LhD2{wJc4!lx*2 zG0Qp7N-d=ZC0(0DN6&XqPhPr06x*ko#3uO~X}+FbBwG|>9O-DtQag1OKodw^%bF2R zxXgb!b11V$*gWbcquad{h>x`YVVffVa_VFMX(d6Q^N@aYPHSE?z_KSw z-6064WZJ)w^a^UJ(y1w?h>l7*$N4=QQ;Xj%N5f#{JQRnxqpIuL(%+m#-JYm$erEFc zYsHK)ui`sn_J(5*{>)8&Fp!8aM}Vu}(=DHjy@j~=^W|Elp;gs4itPO3|YQrda-r3bnTmHw)5e;1RfLe0<&*@yO<-5|h!^0EhR~E?i@s82|vL{{~05FxrMq-Bec&b>9o|g|7 z<}4-$VUX2a90_e6I&btO`U z^Y5WwAG)J*7}>okw%FGzpP#yqIJ3A?J*R6RH4&Zn!V=vYwcF z;V0QP11JO|@V15yrlQCs>1n03N9Jki7v;lRQ{YHwfv);Ks;<-(JAAE5=?#17a46CN z!eeC)OAn41X^uf(l4uU28<-9oO5u~iFH)2fM5(6GubShD(#?zYNv9i$yk{zKR+O)= zxu$@+T$sM9a|;qZGEfx9v3prspxEu4D8e5V3-?fYiDQ6+Ek zM9d@-A2=%3K-AKjb7u=v&X-5b{GPVZQ-{Q{Ji~WsZ7DQ9#UbB~iS)YFRpiDX zdO%UHatl%h-SNrz40ZcG$MabHCBuPrkMxP;Z_bs6xA<0_D}T2wAMF1Te*bRq)GXKy zpKRMPIN}wOlX`Hx2}eOG$WL)5z(i81CaK%wR;jDR^iosp`D z5e{`n=1*>|x-hZj>BE6>476?-Y_q2|Lk(Yo9Wp?!*7UBj<&csb7aEnevR1z4bLv%%gGXA~-ZcCgw8 zQA2@9jVOf(vgp6m`a#@hRwB;oKoXRoC3_H-+^H$3PWV==DkMJ}mB8Mfv&*W+=G@`s zd3b<_!Dc)wPbF%w0*fT+8uqpOLe@+`DD12+hNC`QxPXKZNF(TMRWUB{qg>OsI9{lX zHu14a&dKvC<-Vk)g>R?qh$_?hP!>qsJO~*8bfcap)_ur))g)g4*W4EP9bQ46I8-c; zXk$JfN;jd*`xy(T2Cqmcn%A!Ft1 zB12n8V-#`+Wua+B1pK>=Y~_gLmYC=1o6}W+epmR$3|e=Nr{RqJme{vKgLRE_RL0+V z@j#E>3u}SR7efid{iu0%akfG8V?2@5BFFPB#_{-F<@E5&&!DC)H;-}w<$FHnj4p@d z#GVx~jQDSkSy*S<4C2QEOQt=5R0bcDZn`H?9_d;8v~`=BBTfl@_WSHOucOY@QNAYn*^DNHBd8VsGU8pPc7{+H83=K&a?n5R(xmos6g zoFmTdnkczR4a3L4?|j+mo~YXLkx%xqI;UW%&Ql4@`ujqy1$N#-)@c{U9BzE+Eukf#nUC?)*PiJwf(J%01@TLN}m{9N!`p?A%1SKVv&NdIk zDf>~|A=0}6-!}t+-{ZZ2YrP^8wlHoHe%?!d0n7Utoj-BAFLy`o^ctK+1ab{SDSbr` zM*e{Ro@++Lla%>8_31VC;e=WJK9}H)2khK)-rV)COT=9|fr9&gc!q9)p}(nuXAp-g zxdSwe{_By@8a;kqe^FXJu?>776hD7Am?Q4CM<4soKPOKl2P`834q6;j;6su2$0Y0E z?E>Glgq^v|zTlhNP^|PpTo_Mr+&z{2KX2(E3Dl>faImKD;2@rif`;`?`?dvrzmTRM z&8(wxJ)_ku9umYaSc8zcMH_!m2;LkskZ3kR$TUa81^k&n8VV09J&^OZbc}DyUB4=P z@;x`Nplf(5zt6D-AeWaC)cfwQlOB|_=`FeuMn7qfiahQ%Qd##Th%3Px)}@c6;O1Pa zYdr(T`Do45h*z=|^X=8yoQVB61og%;IevDZ@u*U0! zHg@^%pUGkEF|ra~%bZ*O-36wpm(kmdbd%7bDl~Co{4L~b)+lP+O)i-X1pJC(*$RVprFj3^ys{3g5 zpJ<`(#JQahL^)v!-dLxAX&j1uwy{+&hu{-Pv9MNf1)(cs)3Ro|W zvs2HkRZ0^;)Snj|7RkA**MoAXR~hvRKa^01?^-V)X5`&*r zN<>(F)cvW-lOmXx1-;|BD?^?n z#+Hw0h4=-!FfXN-CBMmz%^=knvAO`oVnaZO=6w+vJt8=-5ghD091i>ym2Tjgl7#F-V`!H}0^6wx zgFa{tkI;bTF4Ew!_fwno6aJQI^yk@BzB4#*SDrEH(}HU6t*Pl9Lzk!A+m4HW%{L-h zilpdx>98I9tIjVgF$@K zN#OW1nrh^bD2TG3Q8%gYstK_We*Az$b0+cZ7wj28;%1#`8){$geLPsTqFO3`-MfVNZOMVoK8(fk}W*P-c zBg=j6=jGMo%#MD~w>;1Z?xNoLT|?001Oq{_KnWOk**)HL2xf&*Uh>AWz68h_EG(!P zLU;K>R8E`JK0xs@3^-1)f?9rBhFoUZdStuWfNxMzi0qK7jA3h`e(pNyBMuaHtMDDA zy@z|8W&*pcbV89UpgNCcv=>*M-B4<&~!k%d}nZdn-;flQwz% zW1(-0!=QUbyqv{K!>#q#dh^I?{I%j(_{_4_(%D)4E{ckWeWpOSe|_x%pzL zx@#rV4yc4QHc0DB6K>yo`)2nWt7w|}A^8>3*l^X4Hyt#cSQ0m`kXrfcRh4LDh}4=r z=FcYx#Z7HO|Cc)6n>mTNPY}ji)eYC)eLtpfE~xm41W!Pv?j*|t$5d|br1jUo>I>@+ zw5A{OK@N9bRD@#MLEoA@!VHTJ;^0jqe}o7K<^lFdI-$6y*y1gN6d0Zr2x$U>U#|Rg z4B(ji{!X_xSeX0hf36B`o!-zM;L!Lc<@1i^IrFhx!eP+nx@Lz_R~^vFC<0|^gs%Ge z&?RLdsSAhyd=o|#!BwCUV#PKVhjG+LC>SGhDl2~g8H0_ZCLhg%XRZaOE*F9{i4$9- zdsGA&gNbWEAtMgtRS!tBj0=Kqh{*U&K;-d_xf)z*oJf^?6pT&sC*+#oR3-rt#5ZPC zOVj_gqa;4c5YhkjzvH2SfKdIX|2^RbD$#fW33vujPq4po=wA;HG?*c+;gN^^;;iAp zp=pa&)ApA|ep`nTS98gjy$dc=m!j^XWz5Yx7tz{e#9cYhrl(<8<8b7ot~+0My_+2_ zJb7&M6eV&}eF|NB<~+auIpOQNyT;Uqtb_PUxDAVv5OJ3kLf@u2uz?NWEEVkEcs+E$ z2Ckv^vYEGwcj33I^Dq>s(n6h>w+ju3r9=A>MwV<$9;7 zD}>&_&zyL;vj@fAd?-->QR;+;F@@1qpv-`$d;GALTJiuTP*3egpeBU+%_EXt(rjH1 z4;Sa`78C30)(!_V>nuwG)~SLs0{nLw=x4kYdCN;|dYQ0+9x0ACU; zC%IWV*H!}pAERM;p=TdE^JVxxS9wp~piA#)++R36`2p(_K8MAk$vQ{hFX*t48OJ`fLxBf(AZ2x9Rs{ zxE}q7hUE}7q)^z$@W85ZQLZVWQJ7up3S8QrMi*U1(AoPTJ-@c5)tKbmh zs3i&|>=+mXifkF0WrtIj4Kvu!N{>9*nq?ZTw@@5l&6hbfwNFR`lYZby!pOCtQW=hw zA^xQw?^j2MjT>;C%_7S@i3i^QVX1AZBDbqHAq9L?TZ~HISjE@&oUY~L=ik!QMmJA& zc&?$(!WdOX=LzW)^GnOAVkDt+j3u$vscWg~*DA@xFnE5q78Q`NH$cNo zeRa5w!rIkKhpFB0Y_Pj^)GuDC!0%`NUsqQi4rTX-^V+vDVaE0*W*TWi6Jabxk;qa+ ziI6QMvX+!4Ava#W*!veJZ|DFrqm=YzLK^wAE`r^z!=>U~OV3Vv_FfD>7J8*YHm%~! z{i2$(ys;3Q^6zJ3svhgcPcu)kzU!`Qa=1Y|cNDv)#f3atToQJP{ONW=!LxkU$Mcld ztLW?k?N7SYmd#;_m4=1Os%ApHx^Ba8;NHH+fy$_A^FXcpJylG%!WgOJf=U^g?f>xJ zXqy#?(DU%4a$^l-_A&!L?_MkfS(|DMT}8TY-Hu{hU4LxZJBW~e)tV{BJt}ZZU8(2q zut_g)!eT95b;k+g?hh01YAv;vLQUutuWJj;O*@3h|bZ*~>T+4tI=&sxe|5=m9Q4zZ8i6EnieuRfWb5(|$n zPd$}$I}g)N;`a$d+11?-_^bj23!vKak6}MnT$rSGxE_h+NiGf+Jc(|vlvajPC`Qn^o zxxQ26T3fy=U-IksLSv<7*>^);AEfAbolc9zY1mK0T6(d*Jno6X54&_6H@@z2F?7!j zsN-u84LoJkqvCdGOZtzs`Y~SU&~@#RySMq{e7o9L7_aPitz^iJi+S?&DBtRd4-#WU z@Xs_@S-45bGyH4l*U^jp`ZEk+$(85;*9(j0fda8H=G2LLlET3$Q?pXCQ86Xj{CYmi zfXBwN7FZKH=?60lLYis%$;h3ERO0QgIL0{JSaA29&Pio2wLE`5zmNxML0){*o%1%P zbvX5$=<4;$f*lqgB~py*gFXuls_9?QPIoS~6nInOeXVImyF<;8ihmhVdb^2xPz1*_ zFn3Gl#4{8D+qW%IHFhlE%RP#{e-7heb1RF0`MQ6P&=qyx%94v&hePEvgec?H>bXid z#|J^Ep4cYtFAMdKUiYHT>uoWd7F`D44mX+wBX+zp@-Y z(uK!`I8GcR)5xTx3Z4SfGe)*;iU>uIX>i;^W`2$PLctdPDpXZ_YgY^<+xCOq;f4l% zd4Wgrmq}c8Pnk1)VjsUZw+!8EsT~{{A`g5e8u9V!EZ$97=zR?N&GR)UZI?+|jnv3YA|K-``Z|OL|#yprTm(2Gyx`%v(yb(pbhK zru@vIzZ3&RHAN#Qx_kv5TG8}VyX~{Z!ySl(Kn>SOlB9+8>99CNnN)?GI1+XvePV6C z!RWlZx%KsH`D&_VYELq8Jd5u5J_|3dG!LO-m)-XD8AnwEb5z4Mb`pGAt1^x8kG03O z9t^B`_aphC^T73n?ehLa)|+7#Zb0?o%D@T)w)Vm0KD{zrLi>YiGD?tplqwb^^?5^R zVQ^cR0OXiN=z=hi7TJuLFi2sdpeA8(lc@(S34_Zb8UWQ#grZQ0DFe2NZ9rT!i0zk! zwn=~iWf;)=cS6mQY*T(f2O?tGW*=4r$j+g`R~RjV6cDkW!pHy^3F1NffE2tc{%(%w zm(Y>*=>0|@ZDFM2IyNYEkQZzoB*3dO*7?XAjS|Aeqrm}OQTPSK!EEhdBwMI3qF%)T z`iN(P<_0(OvUNm(!Vm^BMgFiTn*z!Z8s^Y=qOh!OD>@{%cx%@^TZDAx?4|M410{SqTm#yXk zaz`+b=5}`aRS}nw5iBoT5F>pQ18p_@)vqMSmLEVitr{UQQs>C103t_s%W)9UbHqcy zz^Dz(!8^|pFEd3p00#ocNRWUdU^yy-mN6oPaYsxXkQvwF(gFL&y&zFP&x%v8 z2tZGupne~qFrm+d22K+yavbDi921x!@l`4^Z79|cbezQi6w3rkKKaX(1QZqt`Vs=} zvov82nkJ4U-Ju9x9${_LgxOpx$k8~DoS$tRAir=BIB5d^p>tTXMv((>^gNPf9hjRW zL5-KeK)MDvjhubYDOspG4Ma}4K=d2zWm$0{aynBxpr|aiYcstb{1^|PEdhwm5+T3ZU#=){oFze(jcj+Sc^#n7qTxTE3w{>*{h6KdY89A1M}#@vzJ3Fc VwlMN}`%er%aGR6olj~j${vQ;P=LY}) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 774fae87..744c64d1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6..65dcd68d 100644 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/gradlew.bat b/gradlew.bat index 53a6b238..6689b85b 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/groovy/build.gradle.kts b/groovy/build.gradle.kts index 5a9291c8..b9da3af8 100644 --- a/groovy/build.gradle.kts +++ b/groovy/build.gradle.kts @@ -8,7 +8,7 @@ version = properties("libraryVersion") plugins { java `java-library` - id("org.jetbrains.kotlin.jvm") version "1.7.22" + id("org.jetbrains.kotlin.jvm") version "1.9.20" `maven-publish` id("signing") } @@ -26,14 +26,14 @@ kotlin { jvmToolchain(11) } -val kotlin_version = "1.7.22" +val kotlin_version = "1.9.20" dependencies { implementation(project(":core")) implementation(group = "org.apache.groovy", name = "groovy-all", version = "4.0.11") implementation(group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version = kotlin_version) - implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.1") + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3") implementation(group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version = kotlin_version) implementation(group = "org.slf4j", name = "slf4j-api", version = "2.0.9") diff --git a/groovy/src/test/kotlin/com/simiacryptus/skyenet/GroovyInterpreterTest.kt b/groovy/src/test/kotlin/com/simiacryptus/skyenet/GroovyInterpreterTest.kt index 9be08b56..4f9ece54 100644 --- a/groovy/src/test/kotlin/com/simiacryptus/skyenet/GroovyInterpreterTest.kt +++ b/groovy/src/test/kotlin/com/simiacryptus/skyenet/GroovyInterpreterTest.kt @@ -7,15 +7,5 @@ import org.junit.jupiter.api.Test class GroovyInterpreterTest : HeartTestBase() { override fun newInterpreter(map: java.util.Map) = GroovyInterpreter(map) - @Test - override fun `test validate with tool object and invalid code`() { - // Groovy doesn't support this - } - - @Test - override fun `test validate with undefined variable`() { - // Groovy doesn't support this - } - } diff --git a/java/build.gradle.kts b/java/build.gradle.kts index 579f2ede..3680e1e6 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -8,7 +8,7 @@ version = properties("libraryVersion") plugins { java `java-library` - id("org.jetbrains.kotlin.jvm") version "1.7.22" + id("org.jetbrains.kotlin.jvm") version "1.9.20" `maven-publish` id("signing") } @@ -26,12 +26,12 @@ kotlin { jvmToolchain(11) } -val kotlin_version = "1.7.22" +val kotlin_version = "1.9.20" dependencies { implementation(project(":core")) - implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.1") + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3") implementation(kotlin("stdlib-jdk8")) implementation(group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version = kotlin_version) diff --git a/java/src/main/kotlin/com/simiacryptus/skyenet/heart/JavaInterpreter.kt b/java/src/main/kotlin/com/simiacryptus/skyenet/heart/JavaInterpreter.kt deleted file mode 100644 index 406898d3..00000000 --- a/java/src/main/kotlin/com/simiacryptus/skyenet/heart/JavaInterpreter.kt +++ /dev/null @@ -1,165 +0,0 @@ -package com.simiacryptus.skyenet.heart - -import com.simiacryptus.skyenet.Heart -import java.io.ByteArrayOutputStream -import java.io.OutputStream -import java.net.URI -import java.util.* -import javax.tools.* -import javax.tools.DiagnosticCollector -import javax.tools.JavaFileObject -open class JavaInterpreter(val defs: Map = HashMap()) : Heart { - private val guidMap = HashMap() - - init { - defs.entries.forEach { t -> - val key = t.key - val value = t.value - val guid = UUID.randomUUID().toString() - guidMap.put(guid, value) - System.setProperty("java.def.$key", guid) - } - } - - private val compiler: JavaCompiler = ToolProvider.getSystemJavaCompiler() - override fun getLanguage(): String { - return "Java" - } - - override fun run(code: String): Any? { - val (className, wrappedCode) = wrapJavaCode(code) - val classLoader = InMemoryClassLoader() - val (diagnosticCollector, success) = compile(className, wrappedCode, classLoader) - return wrapExecution { - if (success) { - val clazz = classLoader.loadClass(className) - val method = clazz.getMethod("runSnippet") - try { - method.invoke(null) - } catch (e: Exception) { - throw e.cause ?: e - } - } else { - val errors = diagnosticCollector.diagnostics.joinToString("\n") - Exception("Compilation errors:\n$errors") - } - } - } - - data class Pair(val a: A, val b: B) - - private fun compile( - className: String, - wrappedCode: String, - classLoader: InMemoryClassLoader, - ): Pair, Boolean> { - val compilationUnits = listOf(stringJavaFileObject(className, wrappedCode)) - val diagnosticCollector = DiagnosticCollector() - val fileManager = InMemoryJavaFileManager(compiler, classLoader) - val success = compiler.getTask(null, fileManager, diagnosticCollector, null, null, compilationUnits).call() - return Pair(diagnosticCollector, success) - } - override fun validate(code: String): Exception? { - val (className, wrappedCode) = wrapJavaCode(code) - val classLoader = InMemoryClassLoader() - val (diagnosticCollector, success) = compile(className, wrappedCode, classLoader) - return if (success) { - null - } else { - val errors = diagnosticCollector.diagnostics.joinToString("\n") { it.toString() } - Exception("Compilation errors:\n$errors") - } - } - - - private fun wrapJavaCode(code: String): Pair { - // Class name with random hex - val className = "JavaSnippet" + UUID.randomUUID().toString().replace("-", "") - val definitions = defs.entries.joinToString("\n") { (key, _) -> - val guid = System.getProperty("java.def.$key") - "public static Object $key = guidMap.get(\"$guid\");" - } - - // Split code into import section and code section - val (imports, codeSection) = code.split("\n").partition { it.startsWith("import ") } - - val wrappedCode = """ - |import java.util.Map; - |import java.util.UUID; - |${imports.joinToString("\n|")} - | - |public class $className { - | private static final Map guidMap = initGuidMap(); - | - | $definitions - | - | private static Map initGuidMap() { - | Map map = new java.util.HashMap<>(); - | ${guidMap.entries.joinToString("\n| ") { (guid, _) -> "map.put(\"$guid\", ${className}.class.getClassLoader().loadClass(\"JavaInterpreter\").getMethod(\"getGuidMap\").invoke(null).get(\"$guid\"));" }} - | return map; - | } - | - | public static Object runSnippet() { - | ${codeSection.joinToString("\n| ")} - | return null; - | } - |} - """.trimMargin() - return Pair(className, wrappedCode) - } - - class stringJavaFileObject(private val className: String, private val code: String) : SimpleJavaFileObject( - URI.create( - "string:///" + className.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension - ), JavaFileObject.Kind.SOURCE - ) { - override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence = code - } - - class InMemoryClassLoader : ClassLoader() { - private val classes = mutableMapOf() - - fun addClass(name: String, bytes: ByteArray) { - classes[name] = bytes - } - - override fun findClass(name: String): Class<*> { - val bytes = classes[name] ?: throw ClassNotFoundException(name) - return defineClass(name, bytes, 0, bytes.size) - } - } - - class InMemoryJavaFileManager(private val compiler: JavaCompiler, private val classLoader: InMemoryClassLoader) : - ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) { - override fun getJavaFileForOutput( - location: JavaFileManager.Location?, - className: String, - kind: JavaFileObject.Kind, - sibling: FileObject?, - ): JavaFileObject { - return object : SimpleJavaFileObject( - URI.create( - "string:///" + className.replace( - '.', - '/' - ) + kind.extension - ), kind - ) { - override fun openOutputStream(): OutputStream { - return ByteArrayOutputStream().also { byteArrayOutputStream -> - classLoader.addClass(className, byteArrayOutputStream.toByteArray()) - } - } - } - } - } - - companion object { - @JvmStatic - fun getGuidMap(): MutableMap { - return (Thread.currentThread().contextClassLoader.loadClass("JavaInterpreter").getDeclaredConstructor() - .newInstance() as JavaInterpreter).guidMap - } - } - -} \ No newline at end of file diff --git a/kotlin/META-INF/ModuleName.kotlin_module b/kotlin/META-INF/ModuleName.kotlin_module new file mode 100644 index 0000000000000000000000000000000000000000..1e9f2ca4d180a083a06174d407c30581ce729120 GIT binary patch literal 24 YcmZQzU|?ooU| = mapOf() as java.util.Map, -) : Heart { - - private val engine by lazy { - System.setProperty("kotlin.script.classpath", System.getProperty("java.class.path")) - val engine: ScriptEngine = KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory().scriptEngine - defs.entrySet().forEach { (key, value) -> - engine.put(key, value) - } - engine as KotlinJsr223JvmScriptEngineBase - } - - override fun getLanguage(): String { - return "kotlin" - } - - override fun run(code: String): Any? { - return wrapExecution { engine.eval(wrapCode(code)) } - } - - override fun validate(code: String): Exception? { - return try { - engine.compile(wrapCode(code), engine.context) - null - } catch (e: Exception) { - e - } - } -} - diff --git a/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinInterpreter.kt b/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinInterpreter.kt new file mode 100644 index 00000000..8ac7d33b --- /dev/null +++ b/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinInterpreter.kt @@ -0,0 +1,157 @@ +package com.simiacryptus.skyenet.heart + +import com.simiacryptus.skyenet.Heart +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler +import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot +import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots +import org.jetbrains.kotlin.codegen.CompilationException +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.com.intellij.openapi.Disposable +import org.jetbrains.kotlin.config.CompilerConfiguration +import java.io.File +import java.lang.ref.WeakReference +import java.util.* +import java.util.Map +import kotlin.script.experimental.jsr223.KotlinJsr223DefaultScriptEngineFactory + + +open class KotlinInterpreter( + private val defs: Map = HashMap() as Map +) : Heart { + + override fun validate(code: String): Throwable? { + // Create a temporary file for the code + val tempFile = File.createTempFile("TempKotlin", ".kt").apply { + writeText(code) + deleteOnExit() + } + + // Implement a custom MessageCollector + val errors = ArrayList() + val warnings = ArrayList() + val messageCollector = object : MessageCollector { + override fun clear() {} + override fun hasErrors() = false + override fun report( + severity: CompilerMessageSeverity, + message: String, + location: CompilerMessageSourceLocation? + ) { + // Log the message with severity and location + val line = location!!.line + val column = location.column + val lineText = tempFile.readLines()[line - 1] + val carotText = " ".repeat(column) + "^" + val msg = """ + |$severity: $message at line ${line} column ${column} + | $lineText + | $carotText + """.trimMargin().trim() + log.info(msg) + when { + severity.isError -> errors.add(msg) + severity.isWarning -> warnings.add(msg) + } + } + } + + val configuration = CompilerConfiguration().apply { + put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) + addKotlinSourceRoot(tempFile.absolutePath) + configureJdkClasspathRoots() + System.getProperty("java.class.path").split(File.pathSeparator).forEach { + addJvmClasspathRoot(File(it)) + } + + put(org.jetbrains.kotlin.config.JVMConfigurationKeys.COMPILE_JAVA, true) + put(org.jetbrains.kotlin.config.CommonConfigurationKeys.MODULE_NAME, "ModuleName") + } + + // Create the compiler environment + val environment = KotlinCoreEnvironment.createForProduction( + parentDisposable = Disposable {}, + configuration = configuration, + configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + + val bindingContext = KotlinToJVMBytecodeCompiler.analyze(environment) + ?: throw IllegalStateException("Binding context could not be initialized") + return try { + if (bindingContext.isError()) bindingContext.error else { + val compileBunchOfSources: GenerationState? = + KotlinToJVMBytecodeCompiler.analyzeAndGenerate(environment) + if (null == compileBunchOfSources) { + Exception("Compilation failed") + } else { + if (errors.isEmpty()) null + else RuntimeException( + """ + |${errors.joinToString("\n") { "Error: " + it }} + |${warnings.joinToString("\n") { "Warning: " + it }} + """.trimMargin()) + } + } + } catch (e: CompilationException) { + RuntimeException( + """ + |${e.message} + |${errors.joinToString("\n") { "Error: " + it }} + |${warnings.joinToString("\n") { "Warning: " + it }} + """.trimMargin(), e) + } catch (e: Exception) { + e + } + } + + override fun run(code: String): Any? { + val wrappedCode = wrapCode(code) + log.info( + """Running: + | ${wrappedCode.trimIndent().replace("\n", "\n\t")} + |""".trimMargin() + ) + val scriptEngineFactory = KotlinJsr223DefaultScriptEngineFactory() + return scriptEngineFactory.scriptEngine.eval(wrappedCode) + } + + override fun wrapCode(code: String): String { + val out = ArrayList() + val (imports, otherCode) = code.split("\n").partition { it.trim().startsWith("import ") } + out.addAll(imports) + defs.entrySet().forEach { (key, value) -> + val uuid = storageMap.getOrPut(value) { UUID.randomUUID() } + retrievalIndex.put(uuid, WeakReference(value)) + val fqClassName = KotlinInterpreter.javaClass.name.replace("$", ".") + val typeStr = typeOf(value) + out.add("val $key : $typeStr = $fqClassName.retrievalIndex.get(java.util.UUID.fromString(\"$uuid\"))?.get()!! as $typeStr\n") + } + out.addAll(otherCode) + return out.joinToString("\n") + } + + open fun typeOf(value: Object?): String { + if (value is java.lang.reflect.Proxy) { + return value.javaClass.interfaces[0].name.replace("$", ".") + "?" + } + val replace = value?.javaClass?.name?.replace("$", ".") + return if (replace != null) ("$replace") else "null" + } + + companion object { + val log = org.slf4j.LoggerFactory.getLogger(KotlinInterpreter::class.java) + val storageMap = WeakHashMap() + val retrievalIndex = HashMap>() + } + + override fun getLanguage(): String { + return "Kotlin" + } + +} \ No newline at end of file diff --git a/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinLocalInterpreter.kt b/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinLocalInterpreter.kt deleted file mode 100644 index 54c89288..00000000 --- a/kotlin/src/main/kotlin/com/simiacryptus/skyenet/heart/KotlinLocalInterpreter.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.simiacryptus.skyenet.heart - -import com.simiacryptus.skyenet.Heart -import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes -import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine -import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory -import org.jetbrains.kotlin.script.jsr223.KotlinStandardJsr223ScriptTemplate -import java.io.File -import java.lang.ref.WeakReference -import java.util.* -import javax.script.Bindings -import javax.script.CompiledScript -import javax.script.ScriptContext -import kotlin.reflect.KClass - -open class KotlinLocalInterpreter( - val defs: java.util.Map = HashMap() as java.util.Map -) : Heart { - - private val engine: KotlinJsr223JvmLocalScriptEngine - - init { - System.setProperty("idea.io.use.nio2","true") - val factory = KotlinJsr223JvmLocalScriptEngineFactory() - engine = factory.scriptEngine as KotlinJsr223JvmLocalScriptEngine - val bindings: Bindings? = engine.getBindings(ScriptContext.GLOBAL_SCOPE) - defs.entrySet().forEach { (key, value) -> - engine.put(key, value) - bindings?.put(key, value) - } - } - - override fun validate(code: String): Exception? { - val compile = engine.compile(wrapCode(code), engine.context) - return null - } - - override fun run(code: String): Any? { - return wrapExecution { - val wrapCode = wrapCode(code) - println( - """Running: - | ${wrapCode.trimIndent().replace("\n", "\n\t")} - |""".trimMargin() - ) - val context: ScriptContext = engine.context - val compile: CompiledScript = engine.compile(wrapCode, context) - val eval = compile.eval(context) - eval - } - } - - override fun wrapCode(code: String): String { - val out = ArrayList() - val (imports, otherCode) = code.split("\n").partition { it.startsWith("import ") } - out.addAll(imports) - defs.entrySet().forEach { (key, value) -> - val uuid = storageMap.getOrPut(value) { UUID.randomUUID() } - retrievalIndex.put(uuid, WeakReference(value)) - val fqClassName = KotlinLocalInterpreter.javaClass.name.replace("$", ".") - val typeStr = typeOf(value) - out.add("val $key : $typeStr = $fqClassName.retrievalIndex.get(java.util.UUID.fromString(\"$uuid\"))?.get()!! as $typeStr\n") - } - out.addAll(otherCode) - print(out.joinToString("\n")) - return super.wrapCode(out.joinToString("\n")) - } - - open fun typeOf(value: Object?): String { - if (value is java.lang.reflect.Proxy) { - return value.javaClass.interfaces[0].name.replace("$", ".") + "?" - } - val replace = value?.javaClass?.name?.replace("$", ".") - return if (replace != null) ("$replace") else "null" - } - - companion object { - val storageMap = WeakHashMap() - val retrievalIndex = HashMap>() - } - - override fun getLanguage(): String { - return "Kotlin" - } - -} \ No newline at end of file diff --git a/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinDaemonInterpreterTest.kt b/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinDaemonInterpreterTest.kt deleted file mode 100644 index 469934e7..00000000 --- a/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinDaemonInterpreterTest.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.simiacryptus.skyenet.heart - -import com.simiacryptus.skyenet.HeartTestBase -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test - -//class KotlinDaemonInterpreterTest : HeartTestBase() { -// override fun newInterpreter(map: java.util.Map) = KotlinDaemonInterpreter(map) -//} \ No newline at end of file diff --git a/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinLocalInterpreterTest.kt b/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinInterpreterTest.kt similarity index 53% rename from kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinLocalInterpreterTest.kt rename to kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinInterpreterTest.kt index edbcf3d1..2c5cbfc5 100644 --- a/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinLocalInterpreterTest.kt +++ b/kotlin/src/test/kotlin/com/simiacryptus/skyenet/KotlinInterpreterTest.kt @@ -1,21 +1,15 @@ -package com.simiacryptus.skyenet.heart - -import com.simiacryptus.skyenet.HeartTestBase -import org.junit.jupiter.api.Test -import java.util.zip.ZipFile - -class KotlinLocalInterpreterTest : HeartTestBase() { - - override fun newInterpreter(map: java.util.Map) = KotlinLocalInterpreter(map) - - @Test - override fun `test run with variables`() { - // TODO: This test is failing due to a bug with supplied primitives (e.g. Integer) - } - - @Test - override fun `test validate with variables`() { - // TODO: This test is failing due to a bug with supplied primitives (e.g. Integer) - } - +package com.simiacryptus.skyenet.heart + +import com.simiacryptus.skyenet.HeartTestBase +import org.junit.jupiter.api.Test + +class KotlinInterpreterTest : HeartTestBase() { + + override fun newInterpreter(map: java.util.Map) = KotlinInterpreter(map) + + @Test + override fun `test run with variables`() { + // TODO: This test is failing due to a bug with supplied primitives (e.g. Integer) + } + } \ No newline at end of file diff --git a/kotlin/src/test/resources/logback.xml b/kotlin/src/test/resources/logback.xml new file mode 100644 index 00000000..c755da68 --- /dev/null +++ b/kotlin/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/scala/build.gradle.kts b/scala/build.gradle.kts index 4d23e6d5..5375e7a3 100644 --- a/scala/build.gradle.kts +++ b/scala/build.gradle.kts @@ -10,7 +10,7 @@ plugins { `java-library` `scala` `maven-publish` - id("org.jetbrains.kotlin.jvm") version "1.7.22" + id("org.jetbrains.kotlin.jvm") version "1.9.20" id("signing") } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1e29ea18..cce729ee 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,6 @@ include("java") include("scala") include("kotlin") include("webui") -include("util") +//include("util") //includeBuild("../joe-penai/") diff --git a/util/build.gradle.kts b/util/build.gradle.kts deleted file mode 100644 index c01dd4e1..00000000 --- a/util/build.gradle.kts +++ /dev/null @@ -1,166 +0,0 @@ -import org.gradle.api.tasks.testing.logging.TestLogEvent -import java.net.URI - -fun properties(key: String) = project.findProperty(key).toString() -group = properties("libraryGroup") -version = properties("libraryVersion") - -plugins { - java - `java-library` - `maven-publish` - id("signing") - id("org.jetbrains.kotlin.jvm") version "1.7.22" -} - -repositories { - mavenCentral { - metadataSources { - mavenPom() - artifact() - } - } -} - -kotlin { - jvmToolchain(11) -// jvmToolchain(17) -} - -val kotlin_version = "1.7.22" -val jetty_version = "11.0.17" -val logback_version = "1.2.12" - -dependencies { - - implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.24") - - implementation(project(":core")) - implementation(project(":webui")) - - implementation(kotlin("stdlib-jdk8")) - implementation(group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version = kotlin_version) - implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.1") - - compileOnly(group = "commons-io", name = "commons-io", version = "2.11.0") - compileOnly(group = "org.eclipse.jetty", name = "jetty-server", version = jetty_version) - compileOnly(group = "com.google.cloud", name = "google-cloud-texttospeech", version = "2.28.0") - compileOnly(group = "com.amazonaws", name = "aws-java-sdk", version = "1.12.454") - - implementation(group = "org.slf4j", name = "slf4j-api", version = "2.0.9") - compileOnly(group = "ch.qos.logback", name = "logback-classic", version = logback_version) - compileOnly(group = "ch.qos.logback", name = "logback-core", version = logback_version) - - testImplementation(group = "org.junit.jupiter", name = "junit-jupiter-api", version = "5.9.2") - testRuntimeOnly(group = "org.junit.jupiter", name = "junit-jupiter-engine", version = "5.9.2") - - testImplementation(kotlin("script-runtime")) - -} - -tasks { - - compileKotlin { - kotlinOptions { - javaParameters = true - } - } - compileTestKotlin { - kotlinOptions { - javaParameters = true - } - } - test { - useJUnitPlatform() - systemProperty("surefire.useManifestOnlyJar", "false") - testLogging { - events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - } - jvmArgs( - "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", - "--add-opens", "java.base/java.util=ALL-UNNAMED", - "--add-opens", "java.base/java.lang=ALL-UNNAMED" - ) - } -} - - -val javadocJar by tasks.registering(Jar::class) { - archiveClassifier.set("javadoc") - from(tasks.javadoc) -} - -val sourcesJar by tasks.registering(Jar::class) { - archiveClassifier.set("sources") - from(sourceSets.main.get().allSource) -} - -publishing { - - publications { - create("mavenJava") { - artifactId = "util" - from(components["java"]) - artifact(sourcesJar.get()) - artifact(javadocJar.get()) - versionMapping { - usage("java-api") { - fromResolutionOf("runtimeClasspath") - } - usage("java-runtime") { - fromResolutionResult() - } - } - pom { - name.set("SkyeNet Utilities") - description.set("A very helpful puppy") - url.set("https://github.com/SimiaCryptus/SkyeNet") - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - developers { - developer { - id.set("acharneski") - name.set("Andrew Charneski") - email.set("acharneski@gmail.com") - } - } - scm { - connection.set("scm:git:git://git@github.com/SimiaCryptus/SkyeNet.git") - developerConnection.set("scm:git:ssh://git@github.com/SimiaCryptus/SkyeNet.git") - url.set("https://github.com/SimiaCryptus/SkyeNet") - } - } - } - } - repositories { - maven { - val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - val snapshotsRepoUrl = "https://oss.sonatype.org/mask/repositories/snapshots" - url = URI(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) - credentials { - username = System.getenv("OSSRH_USERNAME") ?: System.getProperty("ossrhUsername") - ?: properties("ossrhUsername") - password = System.getenv("OSSRH_PASSWORD") ?: System.getProperty("ossrhPassword") - ?: properties("ossrhPassword") - } - } - } - if (System.getenv("GPG_PRIVATE_KEY") != null && System.getenv("GPG_PASSPHRASE") != null) afterEvaluate { - signing { - sign(publications["mavenJava"]) - } - } -} - -if (System.getenv("GPG_PRIVATE_KEY") != null && System.getenv("GPG_PASSPHRASE") != null) { - apply() - configure { - useInMemoryPgpKeys(System.getenv("GPG_PRIVATE_KEY"), System.getenv("GPG_PASSPHRASE")) - sign(configurations.archives.get()) - } -} diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/util/AgentDemoBase.kt b/util/src/main/kotlin/com/simiacryptus/skyenet/util/AgentDemoBase.kt deleted file mode 100644 index 42eadb67..00000000 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/util/AgentDemoBase.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.simiacryptus.skyenet.util - -import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.skyenet.Brain -import com.simiacryptus.skyenet.Heart -import com.simiacryptus.skyenet.body.SkyenetCodingSessionServer -import java.awt.Desktop -import java.io.File -import java.net.URI - -abstract class AgentDemoBase { - - abstract fun heart(hands: java.util.Map): Heart - - abstract fun hands(): java.util.Map - - fun runCommand(command: String) { - val hands = hands() - val heart = heart(hands) - val brain = Brain( - api = OpenAIClient(OpenAIClient.keyTxt), - symbols = hands, - language = heart.getLanguage(), - ) - brain.model = OpenAIClient.Models.GPT35Turbo - val response = brain.implement(command) - heart.run(response) - } - - fun launchWebAgent() { - val port = 8080 - val agentDemoBase = this - val server = object : SkyenetCodingSessionServer( - applicationName = "AgentDemo", - oauthConfig = File(File(System.getProperty("user.home")),"client_secret_google_oauth.json").absolutePath, - apiKey = OpenAIClient.keyTxt - ) { - override fun hands(): java.util.Map = agentDemoBase.hands() - override fun heart(hands: java.util.Map): Heart = agentDemoBase.heart(hands) - }.start(port) - Desktop.getDesktop().browse(URI("http://localhost:$port/")) - server.join() - } - -} \ No newline at end of file diff --git a/util/src/main/kotlin/com/simiacryptus/skyenet/util/SystemTools.kt b/util/src/main/kotlin/com/simiacryptus/skyenet/util/SystemTools.kt deleted file mode 100644 index a8ce71b3..00000000 --- a/util/src/main/kotlin/com/simiacryptus/skyenet/util/SystemTools.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.simiacryptus.skyenet.util - -import com.simiacryptus.openai.HttpClientManager -import com.simiacryptus.util.describe.Description -import com.simiacryptus.skyenet.Mouth -import org.apache.http.client.methods.HttpGet -import org.apache.http.util.EntityUtils -import java.io.File -import java.nio.charset.StandardCharsets -import javax.swing.* - -class SystemTools( - googleSpeechKey: String = File(File(System.getProperty("user.home")),"google_speech_api.key.json").absolutePath, -) { - private val mouth = Mouth(googleSpeechKey) - - fun print(text: String) = println(text) - - @Description("Use text-to-speech for audio output") - fun speak(text: String) = mouth.speak(text) - - @Description("Fetch a URL and return the contents as a string") - fun get(url: String): String = _get(url) - - @Description("Prompt the user for input") - fun prompt(text: String) = _prompt(text) - - companion object { - fun _prompt(text: String) { - val jFrame = JFrame() - // Add a label, a single text field, and an OK button. Enter also triggers the OK button. - val okButton = JButton("OK") - val panel = JPanel() - jFrame.contentPane = panel - - panel.add(JLabel(text)) - val textField = JTextField(20) - panel.add(textField) - panel.add(okButton) - - // Set up the action listener for the OK button and the Enter key. - okButton.addActionListener { - jFrame.dispose() - } - textField.addActionListener { - okButton.doClick() - } - - // Set up the JFrame and display it. - jFrame.defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE - jFrame.pack() - jFrame.setLocationRelativeTo(null) - jFrame.isVisible = true - } - - fun _get(url: String): String { - // Use Apache HTTP to fetch the URL - val httpClient = HttpClientManager().getClient() - val httpGet = HttpGet(url) - val httpResponse = httpClient.execute(httpGet) - val entity = httpResponse.entity - val content = EntityUtils.toString(entity, StandardCharsets.UTF_8) - return content - } - } - -} \ No newline at end of file diff --git a/webui/build.gradle.kts b/webui/build.gradle.kts index 814a5144..17a94139 100644 --- a/webui/build.gradle.kts +++ b/webui/build.gradle.kts @@ -8,7 +8,7 @@ version = properties("libraryVersion") plugins { java `java-library` - id("org.jetbrains.kotlin.jvm") version "1.7.22" + id("org.jetbrains.kotlin.jvm") version "1.9.20" `maven-publish` id("signing") } @@ -27,12 +27,12 @@ kotlin { // jvmToolchain(17) } -val kotlin_version = "1.7.22" +val kotlin_version = "1.9.20" val jetty_version = "11.0.17" val jackson_version = "2.15.2" dependencies { - implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.24") + implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.25") implementation(project(":core")) testImplementation(project(":groovy")) @@ -45,10 +45,11 @@ dependencies { implementation(group = "org.eclipse.jetty.websocket", name = "websocket-jetty-server", version = jetty_version) implementation(group = "org.eclipse.jetty.websocket", name = "websocket-jetty-client", version = jetty_version) implementation(group = "org.eclipse.jetty.websocket", name = "websocket-servlet", version = jetty_version) + implementation(group = "org.eclipse.jetty", name = "jetty-webapp", version = jetty_version) implementation(group = "com.vladsch.flexmark", name = "flexmark-all", version = "0.64.8") - implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.1") + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3") implementation(kotlin("stdlib")) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ChatSession.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ChatSession.kt index 73bfb4af..0b388afb 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ChatSession.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ChatSession.kt @@ -6,9 +6,9 @@ abstract class ChatSession( val parent: SkyenetSessionServerBase, sessionId: String, var model: OpenAIClient.Model = OpenAIClient.Models.GPT35Turbo, - var visiblePrompt: String, - var hiddenPrompt: String, - var systemPrompt: String, + private var visiblePrompt: String, + private var hiddenPrompt: String, + private var systemPrompt: String, ) : PersistentSessionBase(sessionId, parent.sessionDataStorage) { init { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ClasspathResource.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ClasspathResource.kt index 528d493e..8e20e851 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ClasspathResource.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/ClasspathResource.kt @@ -16,7 +16,7 @@ class ClasspathResource( val url: URL, @Transient private var useCaches: Boolean = __defaultUseCaches, ) : Resource() { - val classLoader: ClassLoader = this@ClasspathResource.javaClass.classLoader + private val classLoader: ClassLoader = this@ClasspathResource.javaClass.classLoader val path = url.path.split("jar!/").last() override fun close() { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/MacroChatSession.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/MacroChatSession.kt index 09a3957c..9a068bb6 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/MacroChatSession.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/MacroChatSession.kt @@ -11,9 +11,9 @@ class MacroChatSession( sessionId = sessionId, sessionDataStorage = sessionDataStorage ) { - val playSempaphores = mutableMapOf() - val threads = mutableMapOf() - val regenTriggers = mutableMapOf>() + private val playSempaphores = mutableMapOf() + private val threads = mutableMapOf() + private val regenTriggers = mutableMapOf>() val linkTriggers = mutableMapOf>() val txtTriggers = mutableMapOf>() val session : PersistentSessionBase = this diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/OperationStatus.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/OperationStatus.kt index 1c407c78..b50529a0 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/OperationStatus.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/OperationStatus.kt @@ -91,7 +91,7 @@ class OperationStatus @JsonCreator constructor( ) ) } - fun truncate(text: String, length: Int = 500): String { + private fun truncate(text: String, length: Int = 500): String { return if (text.length > length) text.substring(0, length - 3) + "..." else text } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/PersistentSessionBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/PersistentSessionBase.kt index 1e057be3..e7942d6a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/PersistentSessionBase.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/PersistentSessionBase.kt @@ -10,7 +10,7 @@ abstract class PersistentSessionBase( private val messageStates: LinkedHashMap = sessionDataStorage.loadMessages(sessionId), ) : SessionBase(sessionId) { - val messageVersions = HashMap() + private val messageVersions = HashMap() companion object { val logger = org.slf4j.LoggerFactory.getLogger(WebSocketServer::class.java) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSession.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSession.kt index 5928a820..f0ecbd5a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSession.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSession.kt @@ -2,6 +2,8 @@ package com.simiacryptus.skyenet.body import com.simiacryptus.skyenet.Brain import com.simiacryptus.skyenet.OutputInterceptor +import com.simiacryptus.skyenet.servers.SessionServerUtil.getHistory +import com.simiacryptus.skyenet.util.SessionServerUtil open class SkyenetCodingSession( sessionId: String, @@ -30,7 +32,7 @@ open class SkyenetCodingSession( ) { override fun getChatSystemMessages(apiDescription: String) = if (parent.useHistory) { - super.getChatSystemMessages(apiDescription) + SessionServerUtil.getHistory( + super.getChatSystemMessages(apiDescription) + getHistory( history.values, 10, parent.maxHistoryCharacters 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 4e15069c..e66dda1c 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetCodingSessionServer.kt @@ -22,7 +22,7 @@ abstract class SkyenetCodingSessionServer( temperature: Double = 0.1, val model: OpenAIClient.Model = OpenAIClient.Models.GPT4, var useHistory: Boolean = true, - val shortExceptions: Boolean = false, + private val shortExceptions: Boolean = false, val apiKey: String ) : SkyenetSessionServerBase( applicationName = applicationName, 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 d1cb01c0..12ca752d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetInterviewer.kt @@ -9,6 +9,7 @@ import jakarta.servlet.http.HttpServletResponse import org.eclipse.jetty.servlet.ServletHolder import org.eclipse.jetty.webapp.WebAppContext +@Suppress("unused") open class SkyenetInterviewer( applicationName: String, val dataClass: Class, @@ -17,7 +18,7 @@ open class SkyenetInterviewer( oauthConfig: String? = null, val continueSession: (String, T) -> SessionInterface, val validate: (T) -> List, - val apiKey: String + private val apiKey: String ) : SkyenetSessionServerBase( applicationName = applicationName, oauthConfig = oauthConfig, 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 7668f415..ed1f006e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMacroChat.kt @@ -41,6 +41,7 @@ abstract class SkyenetMacroChat( companion object { + @Suppress("unused") fun iterate( sessionUI: SessionUI, sessionDiv: SessionDiv, 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 ef617ace..2ffec480 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/SkyenetMarkupChat.kt @@ -60,7 +60,7 @@ open class SkyenetMarkupChat( val log = org.slf4j.LoggerFactory.getLogger(SkyenetMarkupChat::class.java)!! var sessionDataStorage: SessionDataStorage? = null const val port = 8081 - const val baseURL = "http://localhost:$port" + private const val baseURL = "http://localhost:$port" var skyenet = SkyenetMarkupChat( applicationName = "Chat Demo" ) 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 e4b5169c..cd3b76aa 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/body/WebSocketServer.kt @@ -32,7 +32,7 @@ abstract class WebSocketServer(val resourceBase: String) { inner class MessageWebSocket( val sessionId: String, - val sessionState: SessionInterface, + private val sessionState: SessionInterface, ) : WebSocketAdapter() { override fun onWebSocketConnect(session: Session) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/AppServerBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/AppServerBase.kt new file mode 100644 index 00000000..fd2804f4 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/AppServerBase.kt @@ -0,0 +1,155 @@ +package com.simiacryptus.skyenet.servers + + +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.skyenet.OutputInterceptor +import com.simiacryptus.skyenet.body.AuthenticatedWebsite +import com.simiacryptus.skyenet.body.WebSocketServer +import com.simiacryptus.skyenet.util.AwsUtil.decryptResource +import jakarta.servlet.DispatcherType +import jakarta.servlet.Servlet +import jakarta.servlet.http.HttpServlet +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.server.handler.ContextHandlerCollection +import org.eclipse.jetty.servlet.FilterHolder +import org.eclipse.jetty.servlet.ServletHolder +import org.eclipse.jetty.util.resource.Resource +import org.eclipse.jetty.webapp.WebAppContext +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer +import java.awt.Desktop +import java.net.URI +import java.util.* + +abstract class AppServerBase( + private val localName: String = "localhost", + private val port: Int = 8081, +) { + var domainName: String = "" + abstract val childWebApps: List + + data class ChildWebApp( + val path: String, + val server: WebSocketServer, + val isAuthenticated: Boolean = false + ) + + private fun domainName(isServer: Boolean) = + if (isServer) "https://apps.simiacrypt.us" else "http://$localName:$port" + + protected fun _main(args: Array) { + try { + OutputInterceptor.setupInterceptor() + val isServer = args.contains("--server") + domainName = domainName(isServer) + OpenAIClient.keyTxt = decryptResource("openai.key.kms", javaClass.classLoader) + + val authentication = AuthenticatedWebsite( + redirectUri = "$domainName/oauth2callback", + applicationName = "Demo", + key = { decryptResource("client_secret_google_oauth.json.kms").byteInputStream() } + ) + + val server = start( + port, + *(arrayOf(authentication.configure( + newWebAppContext( + "/", + Resource.newResource(javaClass.classLoader.getResource("welcome")), + object : HttpServlet() { + override fun doGet(req: HttpServletRequest?, resp: HttpServletResponse?) { + resp?.contentType = "text/html" + resp?.writer?.write(""" + + + + + SimiaCryptus Skyenet Apps + + + + + + + + + + """.trimIndent()) + } + } + ), false + ), newWebAppContext("/proxy", ProxyHttpServlet())) + childWebApps.map { + if (it.isAuthenticated) authentication.configure( + newWebAppContext( + it.path, + it.server + ) + ) else newWebAppContext(it.path, it.server) + }) + ) + try { + Desktop.getDesktop().browse(URI("$domainName/")) + } catch (e: Throwable) { + // Ignore + } + server.join() + } catch (e: Throwable) { + e.printStackTrace() + Thread.sleep(1000) + System.exit(1) + } finally { + Thread.sleep(1000) + System.exit(0) + } + } + + private fun start( + port: Int, + vararg webAppContexts: WebAppContext + ): Server { + val contexts = ContextHandlerCollection() + contexts.handlers = webAppContexts.map { + it.addFilter(FilterHolder(CorsFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)) + it + }.toTypedArray() + val server = Server(port) + server.handler = contexts + server.start() + return server + } + + private fun newWebAppContext(path: String, server: WebSocketServer): WebAppContext { + val webAppContext = newWebAppContext(path, server.baseResource ?: throw IllegalStateException("No base resource")) + server.configure(webAppContext, baseUrl = "$domainName/$path") + return webAppContext + } + + private fun newWebAppContext(path: String, baseResource: Resource, indexServlet: Servlet? = null): WebAppContext { + val context = WebAppContext() + JettyWebSocketServletContainerInitializer.configure(context, null) + context.baseResource = baseResource + context.contextPath = path + context.welcomeFiles = arrayOf("index.html") + if (indexServlet != null) { + context.addServlet(ServletHolder("index", indexServlet), "/index.html") + context.addServlet(ServletHolder("index", indexServlet), "/") + } + return context + } + + private fun newWebAppContext(path: String, servlet: Servlet): WebAppContext { + val context = WebAppContext() + JettyWebSocketServletContainerInitializer.configure(context, null) + context.contextPath = path + //context.welcomeFiles = arrayOf("index.html") + context.addServlet(ServletHolder("index", servlet), "/") + return context + } +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CodingActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CodingActorTestApp.kt new file mode 100644 index 00000000..3296db11 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CodingActorTestApp.kt @@ -0,0 +1,54 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.skyenet.actors.CodingActor +import com.simiacryptus.skyenet.body.ChatSessionFlexmark.Companion.renderMarkdown +import com.simiacryptus.skyenet.body.PersistentSessionBase +import com.simiacryptus.skyenet.body.SessionDiv +import com.simiacryptus.skyenet.body.SkyenetMacroChat +import org.slf4j.LoggerFactory +import java.util.* + +open class CodingActorTestApp( + private val actor: CodingActor, + applicationName: String = "CodingActorTest_"+actor.interpreter.javaClass.simpleName, + temperature: Double = 0.3, + oauthConfig: String? = null, +) : SkyenetMacroChat( + applicationName = applicationName, + oauthConfig = oauthConfig, + temperature = temperature, +) { + override fun processMessage( + sessionId: String, + userMessage: String, + session: PersistentSessionBase, + sessionUI: SessionUI, + sessionDiv: SessionDiv + ) { + sessionDiv.append("""
${renderMarkdown(userMessage)}
""", true) + val response = actor.answer(userMessage) + sessionDiv.append("""
${ + renderMarkdown(""" + |```${actor.interpreter.getLanguage().lowercase(Locale.getDefault())} + |${response.getCode()} + |``` + |${sessionUI.hrefLink { + sessionDiv.append("""
Running...
""", true) + val result = response.run() + sessionDiv.append( + """ + |
${result.resultValue}
+ |
${result.resultOutput}
+ """.trimMargin(), false + ) + }}â–¶ + | + """.trimMargin().trim()) + }
""", false) + } + + companion object { + val log = LoggerFactory.getLogger(CodingActorTestApp::class.java) + } + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CorsFilter.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CorsFilter.kt new file mode 100644 index 00000000..2571013c --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/CorsFilter.kt @@ -0,0 +1,28 @@ +package com.simiacryptus.skyenet.servers + +import jakarta.servlet.* +import jakarta.servlet.annotation.WebFilter +import jakarta.servlet.http.HttpServletResponse +import java.io.IOException + +@WebFilter(asyncSupported = true, urlPatterns = ["/*"]) +class CorsFilter : Filter { + @Throws(ServletException::class) + override fun init(filterConfig: FilterConfig?) { + // No initialization needed for this filter + } + + @Throws(IOException::class, ServletException::class) + override fun doFilter(request: ServletRequest?, response: ServletResponse, chain: FilterChain) { + val httpServletResponse = response as HttpServletResponse + httpServletResponse.setHeader("Access-Control-Allow-Origin", "*") + httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT") + httpServletResponse.setHeader("Access-Control-Max-Age", "3600") + httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, authorization") + chain.doFilter(request, response) + } + + override fun destroy() { + // Cleanup any resources if needed + } +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/EmbeddingVisualizer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/EmbeddingVisualizer.kt new file mode 100644 index 00000000..237d19c9 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/EmbeddingVisualizer.kt @@ -0,0 +1,67 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.skyenet.body.* +import com.simiacryptus.util.JsonUtil + +class EmbeddingVisualizer( + val api: OpenAIClient, + val sessionDataStorage: SessionDataStorage, + val sessionID: String, + val appPath: String, + val host: String = "http://localhost:8081", +) { + + private fun toVectorMap(vararg words: String): Map> { + val vectors = words.map {word -> + word to api.createEmbedding( + OpenAIClient.EmbeddingRequest( + model = OpenAIClient.Models.AdaEmbedding.modelName, + input = word, + ) + ).data.first().embedding!! + } + return vectors.toMap() + } + + fun writeTensorflowEmbeddingProjectorHtml(vararg words: String): String { + val vectorMap = toVectorMap(*words.filter { it.isNotBlank() }.toList().toTypedArray()) + val vectorTsv = vectorMap.map { (_, vector) -> + vector.joinToString(separator = "\t") { + "%.2E".format(it) + } + }.joinToString(separator = "\n") + + val metadataTsv = vectorMap.keys.joinToString(separator = "\n") { + it.replace("\n", " ") + } + + val vectorFileName = "vectors.tsv" + val metadataFileName = "metadata.tsv" + val configFileName = "projector-config.json" + sessionDataStorage.getSessionDir(sessionID).resolve(vectorFileName).writeText(vectorTsv) + sessionDataStorage.getSessionDir(sessionID).resolve(metadataFileName).writeText(metadataTsv) + // projector-config.json + val projectorConfig = JsonUtil.toJson( + mapOf( + "embeddings" to listOf( + mapOf( + "tensorName" to "embedding", + "tensorShape" to listOf(vectorMap.values.first().size, 1), + "tensorPath" to "$host/$appPath/fileIndex/$sessionID/$vectorFileName", + "metadataPath" to "$host/$appPath/fileIndex/$sessionID/$metadataFileName", + ) + ) + ) + ) + sessionDataStorage.getSessionDir(sessionID).resolve(configFileName).writeText(projectorConfig) + return """ + Projector Config + Vectors + Metadata + Projector + + """.trimIndent() + } + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ParsedActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ParsedActorTestApp.kt new file mode 100644 index 00000000..10e791be --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ParsedActorTestApp.kt @@ -0,0 +1,44 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.skyenet.actors.ParsedActor +import com.simiacryptus.skyenet.body.ChatSessionFlexmark.Companion.renderMarkdown +import com.simiacryptus.skyenet.body.PersistentSessionBase +import com.simiacryptus.skyenet.body.SessionDiv +import com.simiacryptus.skyenet.body.SkyenetMacroChat +import com.simiacryptus.util.JsonUtil +import org.slf4j.LoggerFactory + +open class ParsedActorTestApp( + private val actor: ParsedActor, + applicationName: String = "ParsedActorTest_"+actor.parserClass.simpleName, + temperature: Double = 0.3, + oauthConfig: String? = null, +) : SkyenetMacroChat( + applicationName = applicationName, + oauthConfig = oauthConfig, + temperature = temperature, +) { + override fun processMessage( + sessionId: String, + userMessage: String, + session: PersistentSessionBase, + sessionUI: SessionUI, + sessionDiv: SessionDiv + ) { + sessionDiv.append("""
${renderMarkdown(userMessage)}
""", true) + val response = actor.answer(userMessage) + sessionDiv.append("""
${ + renderMarkdown(""" + |${response.getText()} + |``` + |${JsonUtil.toJson(response.getObj()!!)} + |``` + """.trimMargin().trim()) + }
""", false) + } + + companion object { + val log = LoggerFactory.getLogger(ParsedActorTestApp::class.java) + } + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ProxyHttpServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ProxyHttpServlet.kt new file mode 100644 index 00000000..7d5519be --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ProxyHttpServlet.kt @@ -0,0 +1,77 @@ +package com.simiacryptus.skyenet.servers + +import jakarta.servlet.http.HttpServlet +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.server.handler.ContextHandlerCollection +import org.eclipse.jetty.servlet.ServletHandler +import org.eclipse.jetty.servlet.ServletHolder +import java.net.HttpURLConnection +import java.net.URI +import java.net.URL + +/** + * A simple reverse proxy that supports the OpenAI API + */ +class ProxyHttpServlet( + private val targetUrl : String = "https://api.openai.com/v1" +) : HttpServlet() { + + override fun service(req: HttpServletRequest, resp: HttpServletResponse) { + val path = req.pathInfo + val url = URI(targetUrl).resolve(path).toString() + println("Proxying $path to $url") + val proxyConnection = URL(url).openConnection() as HttpURLConnection + proxyConnection.requestMethod = req.method + proxyConnection.doOutput = true + proxyConnection.doInput = true + req.headerNames.iterator().forEach { name -> + req.getHeaders(name).iterator().forEach { value -> + proxyConnection.setRequestProperty(name, value) + } + } + proxyConnection.connect() + req.inputStream.copyTo(proxyConnection.outputStream) + proxyConnection.inputStream.copyTo(resp.outputStream) + proxyConnection.disconnect() + } + + companion object { + // main + @JvmStatic + fun main(args: Array) { + test() + } + + fun test() { + // Start a jetty server, and add 2 servlets: the proxy, and a test servlet + val server = Server(8080) + val contextHandlerCollection = ContextHandlerCollection() + val servletHandler = ServletHandler() + servletHandler.server = server + servletHandler.addServletWithMapping(ServletHolder(ProxyHttpServlet("http://localhost:8080")), "/proxy/*") + servletHandler.addServletWithMapping(ServletHolder(object : HttpServlet() { + override fun service(req: HttpServletRequest, resp: HttpServletResponse) { + resp.writer.println("Hello, world!") + } + }), "/test") + contextHandlerCollection.addHandler(servletHandler) + server.handler = contextHandlerCollection + server.start() + // Test the proxy + val connection = URL("http://localhost:8080/proxy/test").openConnection() as HttpURLConnection + connection.requestMethod = "GET" + connection.doOutput = true + connection.doInput = true + connection.connect() + val inputStream = connection.inputStream + val outputStream = System.out + inputStream.copyTo(outputStream) + connection.disconnect() + server.stop() + } + } + + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ReadOnlyApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ReadOnlyApp.kt new file mode 100644 index 00000000..dda32a51 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/ReadOnlyApp.kt @@ -0,0 +1,33 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.skyenet.body.* +import org.slf4j.LoggerFactory + +open class ReadOnlyApp( + applicationName: String, + temperature: Double = 0.3, + oauthConfig: String? = null, +) : SkyenetSessionServerBase( + applicationName = applicationName, + oauthConfig = oauthConfig, + temperature = temperature, +) { + + companion object { + val log = LoggerFactory.getLogger(ReadOnlyApp::class.java) + } + + override val api: OpenAIClient + get() = OpenAIClient() + + override fun newSession(sessionId: String): SessionInterface { + return BasicChatSession( + parent = this@ReadOnlyApp, + model = OpenAIClient.Models.GPT35Turbo, + sessionId = sessionId + ) + } + + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SessionServerUtil.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SessionServerUtil.kt new file mode 100644 index 00000000..ede37ed6 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SessionServerUtil.kt @@ -0,0 +1,40 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.openai.OpenAIClient.ChatMessage +import com.simiacryptus.skyenet.body.OperationStatus + +object SessionServerUtil { + + val logger = org.slf4j.LoggerFactory.getLogger(SessionServerUtil::class.java) + + operator fun java.util.Map.plus(mapOf: Map): java.util.Map { + val hashMap = java.util.HashMap() + this.forEach(hashMap::put) + hashMap.putAll(mapOf) + return hashMap as java.util.Map + } + + fun getHistory(statuses: Collection, maxEntries: Int, maxChars: Int): List { + val messages: java.util.ArrayList = ArrayList() + val operationStatusList = statuses + .filter { it.status != OperationStatus.OperationState.Pending } + .filter { it.status != OperationStatus.OperationState.Running } + .filter { it.status != OperationStatus.OperationState.Implemented } + .filter { !it.instruction.startsWith("!!!") } + .sortedBy { it.created } + .takeLast(maxEntries) + for (operationStatus in operationStatusList) { + messages.addAll(operationStatus.chatMessages) + } + if (messages.isEmpty()) return messages + var totalCharacters = 0 + val truncatedMessages = mutableListOf() + for (message in messages.reversed()) { + totalCharacters += message.content?.length ?: 0 + if (totalCharacters > maxChars) break + truncatedMessages.add(message) + } + return truncatedMessages.reversed() + } + +} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SimpleActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SimpleActorTestApp.kt new file mode 100644 index 00000000..8ac6d9a6 --- /dev/null +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/servers/SimpleActorTestApp.kt @@ -0,0 +1,37 @@ +package com.simiacryptus.skyenet.servers + +import com.simiacryptus.skyenet.actors.SimpleActor +import com.simiacryptus.skyenet.body.ChatSessionFlexmark +import com.simiacryptus.skyenet.body.PersistentSessionBase +import com.simiacryptus.skyenet.body.SessionDiv +import com.simiacryptus.skyenet.body.SkyenetMacroChat +import org.slf4j.LoggerFactory + +open class SimpleActorTestApp( + private val actor: SimpleActor, + applicationName: String = "SimpleActorTest_"+actor.javaClass.simpleName, + temperature: Double = 0.3, + oauthConfig: String? = null, +) : SkyenetMacroChat( + applicationName = applicationName, + oauthConfig = oauthConfig, + temperature = temperature, +) { + + override fun processMessage( + sessionId: String, + userMessage: String, + session: PersistentSessionBase, + sessionUI: SessionUI, + sessionDiv: SessionDiv + ) { + sessionDiv.append("""
${ChatSessionFlexmark.renderMarkdown(userMessage)}
""", true) + val moderatorResponse = actor.answer(userMessage) + sessionDiv.append("""
${ChatSessionFlexmark.renderMarkdown(moderatorResponse)}
""", false) + } + + companion object { + val log = LoggerFactory.getLogger(SimpleActorTestApp::class.java) + } + +} \ No newline at end of file diff --git a/webui/src/main/resources/welcome/chat.css b/webui/src/main/resources/welcome/chat.css new file mode 100644 index 00000000..6ed16ef7 --- /dev/null +++ b/webui/src/main/resources/welcome/chat.css @@ -0,0 +1,252 @@ +#messages { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 40px; + overflow-y: auto; + padding: 10px; + padding-top: 40px; + flex-grow: 1; + flex-shrink: 1; +} + +.message { + background-color: #f0f0f0; + border-radius: 5px; + padding: 10px; + margin-bottom: 10px; + overflow: scroll; + z-index: 100; +} + +#form { + position: absolute; + left: 0; + right: 0; + bottom: 0; + display: flex; + margin: 0; + padding: 5px; + flex-shrink: 0; +} + +.reply-form { + width: 100%; +} + +.chat-input { + resize: vertical; +} + +.reply-input { + resize: vertical; +} + +#message { + flex-grow: 1; + margin-right: 5px; +} + +#disconnected-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 999; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 24px; +} + +#disconnected-overlay p { + font-size: 3rem; + line-height: 1.5; + margin-bottom: 20px; + animation-name: bounce; + animation-duration: 0.5s; + animation-iteration-count: infinite; + animation-direction: alternate; + left: 10%; + position: relative; + color: firebrick; +} + +@keyframes bounce { + 0% { + transform: translateY(0); + } + 100% { + transform: translateY(-10px); + } +} + +.spinner-border { + display: block; + width: 40px; + height: 40px; + border: 4px solid rgba(0, 0, 0, 0.1); + border-left-color: #007bff; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + + +#toolbar { + background-color: #f1f1f1; + padding: 5px; + position: fixed; + top: 0; + width: 100%; + z-index: 1; +} + +#toolbar a { + color: #000; + text-decoration: none; + padding: 5px; + display: block; +} + +#toolbar a:hover { + background-color: #ddd; +} + +/* Modal */ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); +} + +.modal-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; + position: relative; +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; + cursor: pointer; +} + +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; +} + +pre { + white-space: pre-wrap; /* Since CSS modules do not automatically apply this */ +} + +#container { + display: flex; + flex-direction: column; + height: 100vh; /* Adjust this value based on your preferred container height */ +} + +.play-button { + font-size: 48px; + font-weight: bold; + border: none; + background: transparent; + cursor: pointer; + transition: transform 0.1s; +} + +.play-button:focus { + outline: none; +} + +.play-button:active { + transform: scale(0.9); +} + + +.regen-button { + font-size: 48px; + font-weight: bold; + color: chartreuse; + border: none; + background: transparent; + cursor: pointer; + transition: transform 0.1s; +} + +.regen-button:focus { + outline: none; +} + +.regen-button:active { + transform: scale(0.9); +} + +.message-container { + position: relative; /* Add this line to set the position property of the parent div */ +} + +.cancel-button { + position: absolute; + top: 8px; + right: 8px; + font-size: 16px; + font-weight: bold; + border: none; + background: transparent; + cursor: pointer; + transition: transform 0.1s; +} + +.cancel-button:focus { + outline: none; +} + +.cancel-button:active { + transform: scale(0.9); +} + +.collapsible-content { + overflow: hidden; + max-height: 0; + transition: max-height 0.2s ease-out; +} diff --git a/webui/src/main/resources/welcome/favicon.png b/webui/src/main/resources/welcome/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..bec9d99a54d729f50a660141ca5a9f14181ea455 GIT binary patch literal 126746 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ξ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 {} + + override val childWebApps by lazy { + listOf( + ChildWebApp("/test_simple", SimpleActorTestApp(SimpleActor("Translate the user's request into pig latin.", "PigLatin"))), + ChildWebApp("/test_parsed_joke", ParsedActorTestApp(ParsedActor(JokeParser::class.java, "Tell me a joke"))), + ChildWebApp("/test_coding_scala", CodingActorTestApp(CodingActor(ScalaLocalInterpreter::class))), + ChildWebApp("/test_coding_kotlin", CodingActorTestApp(CodingActor(KotlinInterpreter::class))), + ChildWebApp("/test_coding_groovy", CodingActorTestApp(CodingActor(GroovyInterpreter::class))), + )} + + @JvmStatic + fun main(args: Array) { + super._main(args) + } +} + diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt deleted file mode 100644 index 51fb7f8a..00000000 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/CookbookGenerator.kt +++ /dev/null @@ -1,180 +0,0 @@ -package com.simiacryptus.skyenet - -import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.openai.proxy.ChatProxy -import com.simiacryptus.skyenet.body.ChatSessionFlexmark -import com.simiacryptus.skyenet.body.PersistentSessionBase -import com.simiacryptus.skyenet.body.SessionDiv -import com.simiacryptus.skyenet.body.SkyenetMacroChat -import com.simiacryptus.util.JsonUtil -import com.simiacryptus.util.describe.Description -import java.awt.Desktop -import java.net.URI - -class CookbookGenerator( - applicationName: String, - baseURL: String, - temperature: Double = 0.3 -) : SkyenetMacroChat( - applicationName = applicationName, - temperature = temperature -) { - interface CookbookAuthorAPI { - fun parseRecipeSpec(spec: String): RecipeSpec - - data class RecipeSpec( - val title: String, - val cuisine: String, - val targetAudience: TargetAudience, - val ingredients: List, - val equipment: List, - ) - - data class TargetAudience( - val ageGroup: String - ) - - data class WritingStyle( - val targetAudience: TargetAudience, - val notes: MutableMap = mutableMapOf() - ) - - fun createRecipes(spec: RecipeSpec, count: Int): RecipeList - - data class RecipeList( - val recipeList: List - ) - - data class RecipeSummary( - @Description("A very brief description of the recipe for listing/indexing purposes") - val title: String, - @Description("A brief description of the recipe") - val description: String, - @Description("A list of ingredients used; this should be limited to 2-5 items, usually drawn from the available recipe ingredients") - val ingredients: List, - @Description("A list of equipment used; this should be limited to what is strictly needed, and drawn from the available recipe equipment") - val equipment: List, - ) - - data class RecipeDetails( - @Description("A very brief description of the recipe for listing/indexing purposes") - val title: String, - @Description("A narrative description of the relevant cuisine background") - val background: String, - @Description("A narrative description of the recipe") - val description: String, - @Description("A map of ingredients used to the quantities required for the recipe") - val ingredients: Map, - @Description("A map of equipment to quantity required for the recipe") - val equipment: Map, - @Description("A checklist for recipe preparation") - val kitchenSetup: List, - @Description("A list of steps to perform the recipe") - val steps: List, - @Description("A list of ideas for variations on the recipe") - val variations: List, - val supervisionNotes: List, - ) - - data class RecipeStepData( - val step: String, - val observations: List, - ) - - data class RecipeVariation( - val title: String, - val details: String, - @Description("A map of observations to make during the cooking (with expected results)") - val observations: Map, - ) - - fun detailRecipe(recipe: RecipeSummary): RecipeDetails - - - @Description("Create a detailed cookbook writeup") - fun getFullCookbook( - style: WritingStyle, - recipe: RecipeDetails - ): Cookbook - - fun modifyCookbook( - cookbook: String, - usertext: String - ): Cookbook - - data class Cookbook( - val markdown: String - ) - } - - val cookbookAuthorAPI = ChatProxy( - clazz = CookbookAuthorAPI::class.java, - api = api, - model = OpenAIClient.Models.GPT35Turbo, - temperature = temperature - ).create() - - override fun processMessage( - sessionId: String, - userMessage: String, - session: PersistentSessionBase, - sessionUI: SessionUI, - sessionDiv: SessionDiv - ) { - sessionDiv.append("""

$userMessage
""", true) - val spec = cookbookAuthorAPI.parseRecipeSpec(userMessage) - sessionDiv.append("""
${JsonUtil.toJson(spec)}
""", true) - val recipes = cookbookAuthorAPI.createRecipes(spec, 20) - //sessionDiv.apply("""
${JsonUtil.toJson(recipes)}
""", true) - for (recipe in recipes.recipeList.toMutableList().shuffled()) { - sessionDiv.append("""
${JsonUtil.toJson(recipe)}
${sessionUI.hrefLink { - sessionDiv.append("", true) - val details = cookbookAuthorAPI.detailRecipe(recipe) - sessionDiv.append("""
${JsonUtil.toJson(details)}
""", true) - val fullCookbook = cookbookAuthorAPI.getFullCookbook( - style = CookbookAuthorAPI.WritingStyle( - targetAudience = spec.targetAudience, - notes = mutableMapOf( - "description" to "Should be conversational and friendly, introducing the cook to both the recipe and background cuisine", - "instructions" to "Fully detailed instructions, including measurements, amounts, notes, and precautions. Use a conversational style that invites the cook to make predictions and includes explanations.", - "notes" to "Briefly formatted at the bottom to be readable by the adult with full detail but not attention-drawing" - ) - ), - recipe = details, - ) - postRecipe(sessionDiv, fullCookbook, sessionUI, cookbookAuthorAPI) - } }Expand
""", true) - } - } - - private fun postRecipe( - sessionDiv: SessionDiv, - fullCookbook: CookbookAuthorAPI.Cookbook, - sessionUI: SessionUI, - cookbookAuthorAPI: CookbookAuthorAPI - ) { - sessionDiv.append( - """
${ChatSessionFlexmark.renderMarkdown(fullCookbook.markdown)}
${ - sessionUI.textInput { userInput -> - sessionDiv.append("", true) - val cookbook = cookbookAuthorAPI.modifyCookbook(fullCookbook.markdown, userInput) - postRecipe(sessionDiv, cookbook, sessionUI, cookbookAuthorAPI) - } - }""", false) - } - - - companion object { - - const val port = 8081 - const val baseURL = "http://localhost:$port" - - @JvmStatic - fun main(args: Array) { - val httpServer = CookbookGenerator("CookbookGenerator", baseURL).start(port) - Desktop.getDesktop().browse(URI(baseURL)) - httpServer.join() - } - } - -} \ No newline at end of file diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt index d0afe301..4f5edb04 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/DemoAgent.kt @@ -3,7 +3,7 @@ package com.simiacryptus.skyenet import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.skyenet.body.SessionServerUtil.asJava +import com.simiacryptus.skyenet.util.SessionServerUtil.asJava import com.simiacryptus.skyenet.body.SkyenetCodingSessionServer import com.simiacryptus.skyenet.heart.ScalaLocalInterpreter import com.simiacryptus.util.describe.AbbrevWhitelistYamlDescriber diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt deleted file mode 100644 index 49040241..00000000 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/SkyenetScienceBook.kt +++ /dev/null @@ -1,179 +0,0 @@ -package com.simiacryptus.skyenet - -import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.openai.proxy.ChatProxy -import com.simiacryptus.skyenet.body.ChatSessionFlexmark -import com.simiacryptus.skyenet.body.PersistentSessionBase -import com.simiacryptus.skyenet.body.SessionDiv -import com.simiacryptus.skyenet.body.SkyenetMacroChat -import com.simiacryptus.util.JsonUtil -import com.simiacryptus.util.describe.Description -import java.awt.Desktop -import java.net.URI - -class SkyenetScienceBook( - applicationName: String, - baseURL: String, - temperature: Double = 0.3 -) : SkyenetMacroChat( - applicationName = applicationName, - temperature = temperature -) { - interface ScienceAuthorAPI { - fun parseProjectSpec(spec: String): ProjectSpec - - data class ProjectSpec( - val title: String = "", - val domain: String = "", - val targetAudience: TargetAudience? = null, - val materials: List = listOf(), - val equipment: List = listOf(), - ) - - data class TargetAudience( - val ageGroup: String = "", - ) - - data class WritingStyle( - val targetAudience: TargetAudience? = null, - val notes: MutableMap = mutableMapOf() - ) - - fun createExperiments(spec: ProjectSpec, count: Int): ExperimentList - - data class ExperimentList( - val experimentList: List = listOf() - ) - - data class ExperimentSummary( - @Description("A very brief description of the science experiment for listing/indexing purposes") - val title: String = "", - @Description("A brief description of the science experiment") - val description: String = "", - @Description("A list of materials used; this should be limited to 2-5 items, usually drawn from the available project materials") - val materials: List = listOf(), - @Description("A list of equipment used; this should be limited to what is strictly needed, and drawn from the available project materials") - val equipment: List = listOf(), - ) - - data class ExperimentDetails( - @Description("A very brief description of the science experiment for listing/indexing purposes") - val title: String = "", - @Description("A narrative description of the relevant science background") - val background: String = "", - @Description("A narrative description of the science experiment") - val description: String = "", - @Description("A map of materials used to the quantities required for the experiment") - val materials: Map = mapOf(), - @Description("A map of equipment to quantity required for the experiment") - val equipment: Map = mapOf(), - @Description("A checklist for experiment preparation") - val labSetup: List = listOf(), - @Description("A list of steps to perform the experiment") - val steps: List = listOf(), - @Description("A map of observations to make during the experiment (with expected results)") - val observations: Map = mapOf(), - @Description("A list of ideas for variations on the experiment") - val variations: List = listOf(), - val supervisionNotes: List = listOf(), - ) - - data class ExperimentStepData( - val step: String = "", - val observations: List = listOf(), - ) - - data class ExperimentVariation( - val title: String = "", - val details: String = "", - @Description("A map of observations to make during the experiment (with expected results)") - val observations: Map = mapOf(), - ) - - fun detailExperiment(experiment: ExperimentSummary): ExperimentDetails - - @Description("Create a detailed notebook writeup") - fun getFullLabNotebook( - style: WritingStyle, - experiment: ExperimentDetails - ): LabNotebook - - fun modifyNotebook( - notebook: String, - usertext: String - ): LabNotebook - - data class LabNotebook( - val markdown: String = "" - ) - } - - val scienceAuthorAPI = ChatProxy( - clazz = ScienceAuthorAPI::class.java, - api = api, - model = OpenAIClient.Models.GPT35Turbo, - temperature = temperature - ).create() - override fun processMessage( - sessionId: String, - userMessage: String, - session: PersistentSessionBase, - sessionUI: SessionUI, - sessionDiv: SessionDiv - ) { - sessionDiv.append("""
$userMessage
""", true) - val spec = scienceAuthorAPI.parseProjectSpec(userMessage) - sessionDiv.append("""
${JsonUtil.toJson(spec)}
""", true) - val experiments = scienceAuthorAPI.createExperiments(spec, 20) - for (experiment in experiments.experimentList.toMutableList().shuffled()) { - sessionDiv.append("""
${JsonUtil.toJson(experiment)}
${sessionUI.hrefLink { - sessionDiv.append("", true) - val details = scienceAuthorAPI.detailExperiment(experiment) - sessionDiv.append("""
${JsonUtil.toJson(details)}
""", true) - val fullLabNotebook = scienceAuthorAPI.getFullLabNotebook( - style = ScienceAuthorAPI.WritingStyle( - targetAudience = spec.targetAudience, - notes = mutableMapOf( - "description" to "Should be conversational and friendly, introducing the experimenter to both the experiment and background theory", - "instructions" to "Fully detailed instructions, including measurements, amounts, notes, and precautions. Use a conversational style that invites the student to make predictions and includes explanations.", - "notes" to "Briefly formatted at the bottom to be readable by the adult with full detail but not attention-drawing" - ) - ), - experiment = details, - ) - postExperiment(sessionDiv, fullLabNotebook, sessionUI, scienceAuthorAPI) - } }Expand
""", true) - } - } - - private fun postExperiment( - sessionDiv: SessionDiv, - fullLabNotebook: ScienceAuthorAPI.LabNotebook, - sessionUI: SessionUI, - scienceAuthorAPI: ScienceAuthorAPI - ) { - sessionDiv.append( - """
${ChatSessionFlexmark.renderMarkdown(fullLabNotebook.markdown)}
${ - sessionUI.textInput { userInput -> - sessionDiv.append("", true) - val labNotebook = scienceAuthorAPI.modifyNotebook(fullLabNotebook.markdown, userInput) - postExperiment(sessionDiv, labNotebook, sessionUI, scienceAuthorAPI) - } - }""", false) - } - - - companion object { - - const val port = 8771 - const val baseURL = "http://localhost:$port" - - @JvmStatic - fun main(args: Array) { - val httpServer = SkyenetScienceBook("SkyenetScienceBook", baseURL).start(port) - Desktop.getDesktop().browse(URI(baseURL)) - httpServer.join() - } - } - -} \ No newline at end of file diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt deleted file mode 100644 index cad5e273..00000000 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/SoftwareProjectGenerator.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.simiacryptus.skyenet - -import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.openai.proxy.ChatProxy -import com.simiacryptus.skyenet.body.ChatSessionFlexmark -import com.simiacryptus.skyenet.body.PersistentSessionBase -import com.simiacryptus.skyenet.body.SessionDiv -import com.simiacryptus.skyenet.body.SkyenetMacroChat -import com.simiacryptus.util.JsonUtil -import java.awt.Desktop -import java.net.URI - -class SoftwareProjectGenerator( - applicationName: String, - baseURL: String, - temperature: Double = 0.3 -) : SkyenetMacroChat( - applicationName = applicationName, - temperature = temperature -) { - interface ProjectAPI { - - data class ProjectParameters( - val title: String = "", - val description: String = "", - val programmingLanguage: String = "", - val requirements: List = listOf(), - ) - - fun parseProject(projectDescription: String): ProjectParameters - - fun expandProject(project: ProjectParameters): FileSpecList - - data class FileSpecList( - val items: List - ) - - data class FileSpec( - val filepath: String = "", - val requirements: List = listOf(), - ) - - fun implementFile(file: FileSpec): FileImpl - - data class FileImpl( - val filepath: String = "", - val language: String = "", - val text: String = "", - ) - - - } - - val projectAPI = ChatProxy( - clazz = ProjectAPI::class.java, - api = api, - model = OpenAIClient.Models.GPT4, - temperature = temperature - ).create() - - override fun processMessage( - sessionId: String, - userMessage: String, - session: PersistentSessionBase, - sessionUI: SessionUI, - sessionDiv: SessionDiv - ) { - try { - sessionDiv.append("""
${ChatSessionFlexmark.renderMarkdown(userMessage)}
""", true) - val projectParameters = projectAPI.parseProject(userMessage) - sessionDiv.append("""
${JsonUtil.toJson(projectParameters)}
""", true) - //sessionDiv.apply("
${projectParameters.title}
", true) - val fileSpecList = projectAPI.expandProject(projectParameters) - - fileSpecList.items.forEach { fileSpec -> - sessionDiv.append( - """
${ - sessionUI.hrefLink { - sessionDiv.append("
${fileSpec.filepath}
", true) - val fileImpl = projectAPI.implementFile(fileSpec) - sessionDiv.append("
${fileImpl.text}
", false) - } - }${fileSpec.filepath}
""", false) - } - - } catch (e: Throwable) { - logger.warn("Error", e) - } - } - - companion object { - - const val port = 8081 - const val baseURL = "http://localhost:$port" - - @JvmStatic - fun main(args: Array) { - val httpServer = SoftwareProjectGenerator("SoftwareProjectGenerator", baseURL).start(port) - Desktop.getDesktop().browse(URI(baseURL)) - httpServer.join() - } - } -} \ No newline at end of file diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt deleted file mode 100644 index 584d5a04..00000000 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/StoryGenerator.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.simiacryptus.skyenet - -import com.simiacryptus.openai.OpenAIClient -import com.simiacryptus.openai.proxy.ChatProxy -import com.simiacryptus.skyenet.body.ChatSessionFlexmark -import com.simiacryptus.skyenet.body.PersistentSessionBase -import com.simiacryptus.skyenet.body.SessionDiv -import com.simiacryptus.skyenet.body.SkyenetMacroChat -import com.simiacryptus.util.describe.Description -import java.awt.Desktop -import java.net.URI - -class StoryGenerator( - applicationName: String, - baseURL: String, - temperature: Double = 0.3 -) : SkyenetMacroChat( - applicationName = applicationName, - temperature = temperature -) { - interface StoryAPI { - - data class StoryParameters( - val title: String = "", - val shortDescription: String = "", - val writingStyle: String = "", - ) - - fun generateStoryIdeas(storyDescription: String, count: Int = 10): StoryParametersList - - data class StoryParametersList( - val items: List - ) - - @Description( - """ - Seed the story and provide the first page. - The length should be about 600 words, or 3-4 minutes of reading. - Establish a random setting and some characters. - Provide an initial context for the story and the reader's first choice. - This first page should be long enough to be interesting. - """ - ) - fun getFirstStoryPage(story: StoryParameters): StoryPage - - fun accumulateSummary(previousSummary: StorySummary?, pages: List): StorySummary - - data class StorySummary( - val description: String = "", - val notes: List = listOf(), - //val characters: Map = mapOf(), - ) - - data class CharacterSummary( - val description: String = "", - val notes: List = listOf() - ) - - fun nextStoryPage(story: StoryParameters, summary: StorySummary?, prevPages: List, choice: String): StoryPage - - data class StoryPage( - @Description("Full text of page, written in the second person, with markdown formatting. It does not include the user choice prompt.") - val text: String = "", - @Description("A choice of action for the user to select from, written briefly in the first person") - val choices: List = listOf() - ) - - data class Choice( - val text: String = "", - ) - } - - val storyAPI = ChatProxy( - clazz = StoryAPI::class.java, - api = api, - model = OpenAIClient.Models.GPT4, - temperature = temperature - ).create() - - override fun processMessage( - sessionId: String, - userMessage: String, - session: PersistentSessionBase, - sessionUI: SessionUI, - sessionDiv: SessionDiv - ) { - try { - sessionDiv.append("""
${ChatSessionFlexmark.renderMarkdown(userMessage)}
""", true) - val storyParameters = storyAPI.generateStoryIdeas(userMessage) - storyParameters.items.forEach { storyParameters -> - sessionDiv.append( - """
${ - sessionUI.hrefLink { - sessionDiv.append("
${storyParameters.title}
", true) - extracted( - sessionDiv = sessionDiv, - history = listOf(), - storyPage = storyAPI.getFirstStoryPage(storyParameters), - sessionUI = sessionUI, - storyParameters = storyParameters - ) - } - }${storyParameters.title} - ${storyParameters.shortDescription}
""", true) - } - } catch (e: Throwable) { - logger.warn("Error", e) - } - } - - private fun extracted( - sessionDiv: SessionDiv, - history: List, - storyPage: StoryAPI.StoryPage, - sessionUI: SessionUI, - storyParameters: StoryAPI.StoryParameters, - summary: StoryAPI.StorySummary? = null - ) { - var summary = summary - var history = history - if (history.size > 5) { - summary = storyAPI.accumulateSummary(summary, history + storyPage.text) - history = listOf() - } - sessionDiv.append((""" -
${ChatSessionFlexmark.renderMarkdown(storyPage.text)}
-
    - ${ - storyPage.choices.joinToString("\n") { choice -> - sessionDiv.append("", true) - "
  1. ${ - sessionUI.hrefLink { - sessionDiv.append( - """
    ${ChatSessionFlexmark.renderMarkdown(choice.text)}
    """, - true - ) - extracted( - sessionDiv = sessionDiv, - history = history + (storyPage.text + "\n\n" + choice.text), - storyPage = storyAPI.nextStoryPage( - storyParameters, - summary, - history + storyPage.text, - choice.text - ), - sessionUI = sessionUI, - storyParameters = storyParameters, - summary = summary - ) - } - }${choice.text}
  2. " - } - } -
""".trimIndent()), false) - } - - companion object { - - const val port = 8081 - const val baseURL = "http://localhost:$port" - - @JvmStatic - fun main(args: Array) { - val httpServer = StoryGenerator("StoryGenerator", baseURL).start(port) - Desktop.getDesktop().browse(URI(baseURL)) - httpServer.join() - } - } -} \ No newline at end of file diff --git a/webui/src/test/resources/client_secret_google_oauth.json.kms b/webui/src/test/resources/client_secret_google_oauth.json.kms new file mode 100644 index 00000000..226d02f2 --- /dev/null +++ b/webui/src/test/resources/client_secret_google_oauth.json.kms @@ -0,0 +1 @@ +AQICAHhOGtRHF5ZEg5njBnXwdiypgcSjcCLE+WJLmXXvOAvs1AGCY90cyRJ0KBFx1AidV/ClAAACLzCCAisGCSqGSIb3DQEHBqCCAhwwggIYAgEAMIICEQYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAwOepR3rWpA1l4TXa8CARCAggHi63YXpBASefflH+A3YtfosxSI7G7dEyOgZ8sDxjHcDk185ISdrn2MZttLHfqw6PKp3EWfgeBY0Ir/yNrrVpB4VrkDeDqKlT8Up7/XUjsyyHBKL0qOPBu0xaYGFj8G5cn2gwJzgTTAZmX7HpZ3x7TyQIlQtNgjrDnYgSxZFoQotD7gehbm2WXA0evt2aLHS1vptHiSTXxce7xgbwx76nuBjbwzT696vqHOlxzlRZgQ7PR+nIDhH2Z0R3yDZ4DfgsGajx91rwQn9ew9Og7slv4LAM9Ay0GA8SXdAmksLvssv+pHDnADTi3HgBrE24V6CpWClukq3NlDdOFOnSlYz/4z/H09ydP3JK84GXtQ4qXuqnX7LNXtzfklA4gaFb5Y5xVhfXC2iuHhzSHla8djT2Q9dmXcVnZEJKf2bV28EJO6YWiYaGJLJ8+PZMRI+xY1Pu6BU3ZJqPecrfRoJNj8hgWAEh2qCiJbadwibmX1yyXqjjq4naYue05da6ru6+Nho8o0r3c1uF/NVVoCF49ctYh4NDsyiD3kFsIlkV/78d+NOwAKzOdD7pTM6YGVJE40NaC2s+Plt3pJOnoZ0Yq8zWNHT+hVdEUd80LLLYsl4U2sTnCc1dXisaSufIwFCHwdqigTS/k= \ No newline at end of file