diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt index f690a2ed..4c78d26b 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt @@ -310,7 +310,7 @@ open class CodingActor( Role.assistant, """ |```${language.lowercase()} - |${previousCode?.let { /*escapeHtml4*/(it).indent(" ") }} + |${previousCode?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` |""".trimMargin().trim().toContentList() ), @@ -320,7 +320,7 @@ open class CodingActor( |The previous code failed with the following error: | |``` - |${error.message?.trim() ?: ""?.let { /*escapeHtml4*/(it).indent(" ") }} + |${error.message?.trim() ?: ""?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` | |Correct the code and try again. @@ -392,9 +392,9 @@ open class CodingActor( fun getRenderedResponse(respondWithCode: List>, defaultLanguage: String = "") = respondWithCode.joinToString("\n") { when (it.first) { - "code" -> "```$defaultLanguage\n${it.second?.let { /*escapeHtml4*/(it).indent(" ") }}\n```" - "text" -> it.second?.let { /*escapeHtml4*/(it).indent(" ") }.toString() - else -> "```${it.first}\n${it.second?.let { /*escapeHtml4*/(it).indent(" ") }}\n```" + "code" -> "```$defaultLanguage\n${it.second?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```" + "text" -> it.second?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }.toString() + else -> "```${it.first}\n${it.second?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```" } } diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt index 70cca73a..44e3d02c 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt @@ -3,6 +3,6 @@ package com.simiacryptus.skyenet.core.actors import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent class MultiExeption(exceptions: Collection) : RuntimeException( - exceptions.joinToString("\n\n") { "```text\n${/*escapeHtml4*/(it.stackTraceToString()).indent(" ")}\n```" } + exceptions.joinToString("\n\n") { "```text\n${/*escapeHtml4*/(it.stackTraceToString())/*.indent(" ")*/}\n```" } ) { } diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt index 129775f2..6e484465 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt @@ -69,7 +69,7 @@ open class ParsedActor( | |This is an example output: |```json - |${JsonUtil.toJson(exampleInstance!!).indent(" ")} + |${JsonUtil.toJson(exampleInstance!!)/*.indent(" ")*/} |``` | """.trimMargin() diff --git a/gradle.properties b/gradle.properties index 3c6d3091..41430921 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # Gradle Releases -> https://github.com/gradle/gradle/releases libraryGroup = com.simiacryptus.skyenet -libraryVersion = 1.0.59 +libraryVersion = 1.0.60 gradleVersion = 7.6.1 diff --git a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt index 3a698fcc..232a7e6e 100644 --- a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt +++ b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt @@ -1,7 +1,9 @@ package com.github.simiacryptus.aicoder.util +import com.github.simiacryptus.aicoder.util.ApxPatchUtil.patch +import com.github.simiacryptus.aicoder.util.DiffUtil.formatDiff +import com.github.simiacryptus.aicoder.util.DiffUtil.generateDiff import com.simiacryptus.skyenet.AgentPatterns.displayMapInTabs -import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.session.SocketManagerBase @@ -134,24 +136,19 @@ object ApxPatchUtil { } fun SocketManagerBase.addApplyDiffLinks( - code: String, + code: StringBuilder, response: String, - fullPatch: MutableList = mutableListOf(), handle: (String) -> Unit, task: SessionTask, ui: ApplicationInterface? = null, ): String { - val diffPattern = """(?s)(? val diffVal = diffBlock.value val hrefLink = hrefLink("Apply Diff") { try { - if (fullPatch.contains(diffVal)) return@hrefLink - fullPatch.add(diffVal) - val newCode = fullPatch.fold(code) { lines, patch -> - ApxPatchUtil.patch(lines, patch) - } + val newCode = patch(code.toString(), diffVal) handle(newCode) task.complete("""
Diff Applied
""") } catch (e: Throwable) { @@ -160,11 +157,9 @@ fun SocketManagerBase.addApplyDiffLinks( } val reverseHrefLink = hrefLink("(Bottom to Top)") { try { - if (fullPatch.contains(diffVal)) return@hrefLink - fullPatch.add(diffVal) val reversedCode = code.lines().reversed().joinToString("\n") val reversedDiff = diffVal.lines().reversed().joinToString("\n") - val newReversedCode = ApxPatchUtil.patch(reversedCode, reversedDiff) + val newReversedCode = patch(reversedCode, reversedDiff) val newCode = newReversedCode.lines().reversed().joinToString("\n") handle(newCode) task.complete("""
Diff Applied (Bottom to Top)
""") @@ -172,7 +167,29 @@ fun SocketManagerBase.addApplyDiffLinks( task.error(ui, e) } } - markdown.replace(diffVal, diffVal + "\n" + hrefLink + "\n" + reverseHrefLink) + val test1 = formatDiff( + generateDiff( + code.lines(), + patch(code.toString(), diffVal).lines() + ) + ) + val test2 = formatDiff( + generateDiff( + code.lines(), + patch( + code.lines().reversed().joinToString("\n"), + diffVal.lines().reversed().joinToString("\n") + ).lines().reversed() + ) + ) + val newValue = displayMapInTabs( + mapOf( + "Diff" to renderMarkdown(diffVal, ui = ui, tabs = true), + "Verify" to renderMarkdown("```diff\n$test1\n```", ui = ui, tabs = true), + "Reverse" to renderMarkdown("```diff\n$test2\n```", ui = ui, tabs = true), + ), ui = ui + ) + "\n" + hrefLink + "\n" + reverseHrefLink + markdown.replace(diffVal, newValue) } return withLinks } @@ -196,7 +213,7 @@ fun SocketManagerBase.addSaveLinks( task.error(null, e) } } - markdown.replace(codeValue + "```", codeValue?.let { /*escapeHtml4*/(it).indent(" ") } + "```\n" + hrefLink) + markdown.replace(codeValue + "```", codeValue?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ } + "```\n" + hrefLink) } return withLinks } @@ -280,24 +297,28 @@ fun SocketManagerBase.addApplyDiffLinks2( markdown.replace( codeblockRaw, displayMapInTabs( mapOf( - "New" to renderMarkdown(codeblockRaw), - "Old" to renderMarkdown(""" + "New" to renderMarkdown(codeblockRaw, ui = ui), + "Old" to renderMarkdown( + """ |```${codeLang} |${prevCode} |``` - """.trimMargin()), - "Patch" to renderMarkdown(""" + """.trimMargin(), ui = ui + ), + "Patch" to renderMarkdown( + """ |```diff |${ - DiffUtil.formatDiff( - DiffUtil.generateDiff( - prevCode.lines(), - codeValue.lines() + formatDiff( + generateDiff( + prevCode.lines(), + codeValue.lines() + ) ) - ) - } + } |``` - """.trimMargin()), + """.trimMargin(), ui = ui + ), ) ) + "\n" + hrefLink ) @@ -316,16 +337,16 @@ private fun SocketManagerBase.renderDiffBlock( ): String { val filepath = path(root, filename) val prevCode = load(filepath, root, code) - val newCode = ApxPatchUtil.patch(prevCode, diffVal) + val newCode = patch(prevCode, diffVal) val echoDiff = try { - DiffUtil.formatDiff( - DiffUtil.generateDiff( + formatDiff( + generateDiff( prevCode.lines(), newCode.lines() ) ) } catch (e: Throwable) { - renderMarkdown("```\n${e.stackTraceToString()}\n```") + renderMarkdown("```\n${e.stackTraceToString()}\n```", ui = ui) } val hrefLink = hrefLink("Apply Diff") { @@ -337,7 +358,7 @@ private fun SocketManagerBase.renderDiffBlock( } handle( mapOf( - relativize.toString() to ApxPatchUtil.patch( + relativize.toString() to patch( prevCode, diffVal ) @@ -354,7 +375,7 @@ private fun SocketManagerBase.renderDiffBlock( val reversedDiff = diffVal.lines().reversed().joinToString("\n") val newReversedCodeMap = reversedCodeMap.mapValues { (file, prevCode) -> if (filename == file) { - ApxPatchUtil.patch(prevCode, reversedDiff).lines().reversed().joinToString("\n") + patch(prevCode, reversedDiff).lines().reversed().joinToString("\n") } else prevCode } handle(newReversedCodeMap) @@ -363,10 +384,10 @@ private fun SocketManagerBase.renderDiffBlock( task.error(null, e) } } - val diffTask = ui?.newTask() - val prevCodeTask = ui?.newTask() - val newCodeTask = ui?.newTask() - val patchTask = ui?.newTask() + val diffTask = ui?.newTask(root = false) + val prevCodeTask = ui?.newTask(root = false) + val newCodeTask = ui?.newTask(root = false) + val patchTask = ui?.newTask(root = false) val inTabs = displayMapInTabs( mapOf( "Diff" to (diffTask?.placeholder ?: ""), @@ -376,10 +397,20 @@ private fun SocketManagerBase.renderDiffBlock( ) ) SocketManagerBase.scheduledThreadPoolExecutor.schedule({ - diffTask?.add(renderMarkdown(/*escapeHtml4*/(diffVal))) - newCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${newCode}\n```")) - prevCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${prevCode}\n```")) - patchTask?.add(renderMarkdown("# $filename\n\n```diff\n ${echoDiff}\n```")) + diffTask?.add(renderMarkdown(/*escapeHtml4*/(diffVal), ui = ui)) + newCodeTask?.add( + renderMarkdown( + "# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${newCode}\n```", + ui = ui + ) + ) + prevCodeTask?.add( + renderMarkdown( + "# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${prevCode}\n```", + ui = ui + ) + ) + patchTask?.add(renderMarkdown("# $filename\n\n```diff\n ${echoDiff}\n```", ui = ui)) }, 100, TimeUnit.MILLISECONDS) val newValue = inTabs + "\n" + hrefLink + "\n" + reverseHrefLink return newValue diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/Acceptable.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/Acceptable.kt index 6b55799b..f881288c 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/Acceptable.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/Acceptable.kt @@ -125,13 +125,13 @@ class Acceptable( + "" + prevValue.substringAfter("") + "
" - + renderMarkdown(userResponse) + + renderMarkdown(userResponse, ui=ui) + "
") tabContent.set(newValue) task.add("") // Show spinner tabs.update() val newDesign = reviseResponse(history) - val newTask = ui.newTask() + val newTask = ui.newTask(root = false) tabContent.set(newValue + "\n" + newTask.placeholder) tabs.update() task.complete() diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/AgentPatterns.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/AgentPatterns.kt index 078a7146..98bf195e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/AgentPatterns.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/AgentPatterns.kt @@ -1,35 +1,50 @@ package com.simiacryptus.skyenet -import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent +import com.simiacryptus.skyenet.webui.application.ApplicationInterface object AgentPatterns { + private val scheduledThreadPoolExecutor = java.util.concurrent.Executors.newScheduledThreadPool(1) fun displayMapInTabs( map: Map, - ) = """ + ui: ApplicationInterface? = null, + split: Boolean = map.entries.map { it.value.length + it.key.length }.sum() > 10000 + ) : String = if(split && ui != null) { + val tasks = map.entries.map { (key, value) -> + key to ui.newTask(root = false) + }.toMap() + scheduledThreadPoolExecutor.schedule({ + tasks.forEach { (key, task) -> + task.complete(map[key]!!) + } + }, 200, java.util.concurrent.TimeUnit.MILLISECONDS) + displayMapInTabs(tasks.mapValues { it.value.placeholder }, ui=ui, split = false) + } else { + """ |
|
|${ - map.keys.joinToString("\n") { key -> - """""" - }.indent(" ")} + map.keys.joinToString("\n") { key -> + """""" + }/*.indent(" ")*/ + } |
|${ - map.entries.withIndex().joinToString("\n") { (idx, t) -> - val (key, value) = t - """ + map.entries.withIndex().joinToString("\n") { (idx, t) -> + val (key, value) = t + """ |
"" - } - }" data-tab="$key"> - |${value.indent(" ")} + when { + idx == 0 -> " active" + else -> "" + } + }" data-tab="$key"> + |${value/*.indent(" ")*/} |
""".trimMargin() - }.indent(" ") - } + }/*.indent(" ")*/ + } |
""".trimMargin() - + } } \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt index 9ea16216..04f73830 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt @@ -9,7 +9,6 @@ import com.simiacryptus.skyenet.Retryable import com.simiacryptus.skyenet.core.actors.ActorSystem import com.simiacryptus.skyenet.core.actors.CodingActor import com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult -import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent import com.simiacryptus.skyenet.core.platform.ApplicationServices import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import com.simiacryptus.skyenet.core.platform.Session @@ -63,7 +62,7 @@ open class CodingAgent( userMessage: String, ) { try { - mainTask.echo(renderMarkdown(userMessage)) + mainTask.echo(renderMarkdown(userMessage, ui=ui)) val codeRequest = codeRequest(listOf(userMessage to ApiModel.Role.user)) start(codeRequest, mainTask) } catch (e: Throwable) { @@ -76,10 +75,10 @@ open class CodingAgent( codeRequest: CodingActor.CodeRequest, task: SessionTask = mainTask, ) { - val newTask = ui.newTask() + val newTask = ui.newTask(root = false) task.complete(newTask.placeholder) Retryable(ui, newTask) { - val newTask = ui.newTask() + val newTask = ui.newTask(root = false) scheduledThreadPoolExecutor.schedule({ cachedThreadPoolExecutor.submit { val statusSB = newTask.add("Running...") @@ -150,7 +149,7 @@ open class CodingAgent( renderMarkdown( response.renderedResponse ?: //language=Markdown - "```${actor.language.lowercase(Locale.getDefault())}\n${response.code.trim()}\n```" + "```${actor.language.lowercase(Locale.getDefault())}\n${response.code.trim()}\n```", ui=ui ) ) } @@ -246,7 +245,7 @@ open class CodingAgent( response: CodeResult ) { try { - task.echo(renderMarkdown(feedback)) + task.echo(renderMarkdown(feedback, ui=ui)) start(codeRequest = codeRequest( messages = request.messages + listOf( @@ -285,14 +284,14 @@ open class CodingAgent( response: CodeResult ) { val message = when { - e is ValidatedObject.ValidationError -> renderMarkdown(e.message ?: "") + e is ValidatedObject.ValidationError -> renderMarkdown(e.message ?: "", ui=ui) e is CodingActor.FailedToImplementException -> renderMarkdown( """ |**Failed to Implement** | |${e.message} | - |""".trimMargin() + |""".trimMargin(), ui=ui ) else -> renderMarkdown( @@ -300,9 +299,9 @@ open class CodingAgent( |**Error `${e.javaClass.name}`** | |```text - |${e.stackTraceToString().indent(" ")} + |${e.stackTraceToString()/*.indent(" ")*/} |``` - |""".trimMargin() + |""".trimMargin(), ui=ui ) } task.add(message, true, "div", "error") @@ -325,23 +324,23 @@ open class CodingAgent( resultValue.isBlank() || resultValue.trim().lowercase() == "null" -> """ |# Output |```text - |${resultOutput.let { /*escapeHtml4*/(it).indent(" ") }} + |${resultOutput.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` """.trimMargin() else -> """ |# Result |``` - |${resultValue.let { /*escapeHtml4*/(it).indent(" ") }} + |${resultValue.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` | |# Output |```text - |${resultOutput.let { /*escapeHtml4*/(it).indent(" ") }} + |${resultOutput.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` """.trimMargin() } - task.add(renderMarkdown(result)) + task.add(renderMarkdown(result, ui=ui)) return result } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/ShellToolAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/ShellToolAgent.kt index cdc2aec2..b7032298 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/ShellToolAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/ShellToolAgent.kt @@ -132,11 +132,11 @@ abstract class ShellToolAgent( } """.trimIndent() val messages = listOf( - "Shell Code: \n```${actor.language}\n${(response.code).indent(" ")}\n```" to ApiModel.Role.assistant, + "Shell Code: \n```${actor.language}\n${(response.code)/*.indent(" ")*/}\n```" to ApiModel.Role.assistant, ) + (lastResult?.let { listOf( - "Example Output:\n\n```text\n${it.indent(" ")}\n```" to ApiModel.Role.assistant + "Example Output:\n\n```text\n${it/*.indent(" ")*/}\n```" to ApiModel.Role.assistant ) } ?: listOf()) + listOf( - "Schema: \n```kotlin\n${schemaCode.indent(" ")}\n```" to ApiModel.Role.assistant, + "Schema: \n```kotlin\n${schemaCode/*.indent(" ")*/}\n```" to ApiModel.Role.assistant, "Implement a parsing method to convert the shell output to the requested data structure" to ApiModel.Role.user ) displayCodeFeedback( @@ -160,7 +160,7 @@ abstract class ShellToolAgent( var openAPI = openAPIParsedActor().getParser(api).apply(servletImpl).let { openApi -> openApi.copy(paths = openApi.paths?.mapKeys { toolsPrefix + it.key.removePrefix(toolsPrefix) }) } - task.add(renderMarkdown("```json\n${JsonUtil.toJson(openAPI).indent(" ")}\n```")) + task.add(renderMarkdown("```json\n${JsonUtil.toJson(openAPI)/*.indent(" ")*/}\n```", ui=ui)) for (i in 0..5) { try { OpenAPIGenerator.main( @@ -185,7 +185,7 @@ abstract class ShellToolAgent( |${e.errors.joinToString("\n") { "ERROR:" + it.toString() }} |${e.warnings.joinToString("\n") { "WARN:" + it.toString() }} """.trimIndent() - task.hideable(ui, renderMarkdown("```\n${error?.let { /*escapeHtml4*/(it).indent(" ") }}\n```")) + task.hideable(ui, renderMarkdown("```\n${error?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui=ui)) openAPI = openAPIParsedActor().answer( listOf( servletImpl, @@ -196,7 +196,7 @@ abstract class ShellToolAgent( val paths = HashMap(openApi.paths) openApi.copy(paths = paths.mapKeys { toolsPrefix + it.key.removePrefix(toolsPrefix) }) } - task.hideable(ui, renderMarkdown("```json\n${JsonUtil.toJson(openAPI).indent(" ")}\n```")) + task.hideable(ui, renderMarkdown("```json\n${JsonUtil.toJson(openAPI)/*.indent(" ")*/}\n```", ui=ui)) } } if (ApplicationServices.authorizationManager.isAuthorized( @@ -306,7 +306,7 @@ abstract class ShellToolAgent( response: CodeResult = execWrap { actor.answer(request, api = api) }, onComplete: (String) -> Unit ) { - task.hideable(ui, renderMarkdown("```kotlin\n${response.code?.let { /*escapeHtml4*/(it).indent(" ") }}\n```")) + task.hideable(ui, renderMarkdown("```kotlin\n${response.code?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui=ui)) val formText = StringBuilder() var formHandle: StringBuilder? = null formHandle = task.add( @@ -367,7 +367,7 @@ abstract class ShellToolAgent( super.responseAction(task, "Revising...", formHandle!!, formText) { //val task = super.ui.newTask() try { - task.echo(renderMarkdown(feedback)) + task.echo(renderMarkdown(feedback, ui=ui)) val codeRequest = CodingActor.CodeRequest( messages = request.messages + listOf( @@ -434,7 +434,7 @@ abstract class ShellToolAgent( ) // if ```html unwrap if (testPage.contains("```html")) testPage = testPage.substringAfter("```html").substringBefore("```") - task.add(renderMarkdown("```html\n${testPage?.let { /*escapeHtml4*/(it).indent(" ") }}\n```")) + task.add(renderMarkdown("```html\n${testPage?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui=ui)) task.complete( " architectureDiscussionActor.answer(toInput(it), api = api) }, outputFn = { design: ParsedResponse -> - // renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj).indent(" ")}\n```") + // renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj)/*.indent(" ")*/}\n```") AgentPatterns.displayMapInTabs( mapOf( - "Text" to renderMarkdown(design.text), - "JSON" to renderMarkdown("```json\n${JsonUtil.toJson(design.obj).indent(" ")}\n```"), + "Text" to renderMarkdown(design.text, ui=ui), + "JSON" to renderMarkdown("```json\n${JsonUtil.toJson(design.obj)/*.indent(" ")*/}\n```", ui=ui), ) ) }, @@ -191,7 +190,7 @@ class WebDevAgent( .joinToString("\n\n") { it?.let { JsonUtil.toJson(it.openApiDescription) } ?: "" } var messageWithTools = userMessage if (toolSpecs.isNotBlank()) messageWithTools += "\n\nThese services are available:\n$toolSpecs" - task.echo(renderMarkdown("```json\n${JsonUtil.toJson(architectureResponse.obj).indent(" ")}\n```")) + task.echo(renderMarkdown("```json\n${JsonUtil.toJson(architectureResponse.obj)/*.indent(" ")*/}\n```", ui=ui)) architectureResponse.obj.resources.filter { !it.path!!.startsWith("http") }.forEach { (path, description) -> @@ -243,8 +242,8 @@ class WebDevAgent( // Apply codeReviewer fun codeSummary() = codeFiles.entries.joinToString("\n\n") { (path, code) -> "# $path\n```${ - /*escapeHtml4*/(path.split('.').last()).indent(" ") - }\n${/*escapeHtml4*/(code).indent(" ")}\n```" + /*escapeHtml4*/(path.split('.').last())/*.indent(" ")*/ + }\n${/*escapeHtml4*/(code)/*.indent(" ")*/}\n```" } fun outputFn(task: SessionTask, design: String): StringBuilder? { @@ -277,7 +276,7 @@ class WebDevAgent( } try { var task = ui.newTask() - task.add(message = renderMarkdown(codeSummary())) + task.add(message = renderMarkdown(codeSummary(), ui=ui)) var design = codeReviewer.answer(listOf(element = codeSummary()), api = api) outputFn(task, design) var textInputHandle: StringBuilder? = null @@ -288,9 +287,9 @@ class WebDevAgent( textInputHandle?.clear() task.complete() task = ui.newTask() - task.echo(renderMarkdown(userResponse)) + task.echo(renderMarkdown(userResponse, ui=ui)) val codeSummary = codeSummary() - task.add(renderMarkdown(codeSummary)) + task.add(renderMarkdown(codeSummary, ui=ui)) design = codeReviewer.respond( messages = codeReviewer.chatMessages( listOf( @@ -332,7 +331,7 @@ class WebDevAgent( if (code.contains("```$language")) code = code.substringAfter("```$language").substringBefore("```") } try { - task.add(renderMarkdown("```${languages.first()}\n$code\n```")) + task.add(renderMarkdown("```${languages.first()}\n$code\n```", ui=ui)) task.add("$path Updated") codeFiles[path] = code val request1 = (request.toList() + @@ -362,7 +361,7 @@ class WebDevAgent( responseAction(task, "Revising...", formHandle!!, formText) { //val task = ui.newTask() try { - task.echo(renderMarkdown(feedback)) + task.echo(renderMarkdown(feedback, ui=ui)) draftResourceCode( task, (request1.toList() + listOf( code to ApiModel.Role.assistant, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationInterface.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationInterface.kt index 24290718..44431ec1 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationInterface.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationInterface.kt @@ -28,7 +28,8 @@ open class ApplicationInterface(val socketManager: SocketManagerBase) { @Description("Creates a new 'task' that can be used to display the progress of a long-running operation.") open fun newTask( //cancelable: Boolean = false - ): SessionTask = socketManager.newTask(false) + root: Boolean = true + ): SessionTask = socketManager.newTask(cancelable = false, root = root) companion object { fun oneAtATime(handler: Consumer): Consumer { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SessionTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SessionTask.kt index 789cf6aa..22872156 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SessionTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SessionTask.kt @@ -3,11 +3,9 @@ package com.simiacryptus.skyenet.webui.session import com.simiacryptus.jopenai.describe.Description import com.simiacryptus.jopenai.proxy.ValidatedObject import com.simiacryptus.skyenet.core.actors.CodingActor -import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent import com.simiacryptus.skyenet.core.platform.StorageInterface.Companion.long64 import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown -import org.apache.commons.text.StringEscapeUtils.escapeHtml4 import org.slf4j.LoggerFactory import java.awt.image.BufferedImage @@ -133,10 +131,10 @@ abstract class SessionTask( | |Stack Trace: |```text - |${e.stackTraceTxt.indent(" ")} + |${e.stackTraceTxt/*.indent(" ")*/} |``` | - |""".trimMargin() + |""".trimMargin(), ui=ui ) e is CodingActor.FailedToImplementException -> renderMarkdown( """ @@ -146,15 +144,15 @@ abstract class SessionTask( | |Prefix: |```${e.language?.lowercase() ?: ""} - |${/*escapeHtml4*/(e.prefix?.indent(" ") ?: "")} + |${/*escapeHtml4*/(e.prefix/*?.indent(" ")*/ ?: "")} |``` | |Implementation Attempt: |```${e.language?.lowercase() ?: ""} - |${/*escapeHtml4*/(e.code?.indent(" ") ?: "")} + |${/*escapeHtml4*/(e.code/*?.indent(" ")*/ ?: "")} |``` | - |""".trimMargin() + |""".trimMargin(), ui=ui ) else -> renderMarkdown( @@ -162,9 +160,9 @@ abstract class SessionTask( |**Error `${e.javaClass.name}`** | |```text - |${e.stackTraceToString()?.let { /*escapeHtml4*/(it).indent(" ") }} + |${e.stackTraceToString()?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} |``` - |""".trimMargin() + |""".trimMargin(), ui=ui ) }, showSpinner, tag, "error" ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt index d36bc50f..1689dfb6 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt @@ -76,9 +76,10 @@ abstract class SocketManagerBase( } fun newTask( - cancelable: Boolean = false + cancelable: Boolean = false, + root : Boolean = true ): SessionTask { - val operationID = randomID() + val operationID = randomID(root) var responseContents = divInitializer(operationID, cancelable) send(responseContents) return SessionTaskImpl(operationID, responseContents, SessionTask.spinner) @@ -248,10 +249,13 @@ abstract class SocketManagerBase( companion object { private val log = LoggerFactory.getLogger(ChatServer::class.java) - private val range = ('a'..'z').toList().toTypedArray() - fun randomID(): String { + private val range1 = ('a'..'y').toList().toTypedArray() + private val range2 = range1 + 'z' + fun randomID(root : Boolean = true): String { val random = java.util.Random() - return (0..5).map { range[random.nextInt(range.size)] }.joinToString("") + val joinToString = (if (root) range1[random.nextInt(range1.size)] else "z").toString() + + (0..4).map { range2[random.nextInt(range2.size)] }.joinToString("") + return joinToString } fun divInitializer(operationID: String = randomID(), cancelable: Boolean): String = diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt index 38c52ad2..dc0e8219 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt @@ -33,7 +33,7 @@ open class CodingActorTestApp( (api as ClientManager.MonitoredClient).budget = 2.00 val message = ui.newTask() try { - message.echo(renderMarkdown(userMessage)) + message.echo(renderMarkdown(userMessage, ui=ui)) val response = actor.answer(CodingActor.CodeRequest(listOf(userMessage to ApiModel.Role.user)), api = api) val canPlay = ApplicationServices.authorizationManager.isAuthorized( this::class.java, @@ -56,10 +56,10 @@ open class CodingActorTestApp( renderMarkdown( """ |```${actor.language.lowercase(Locale.getDefault())} - |${/*escapeHtml4*/(response.code).indent(" ")} + |${/*escapeHtml4*/(response.code)/*.indent(" ")*/} |``` |$playLink - """.trimMargin().trim() + """.trimMargin().trim(), ui=ui ) ) } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ImageActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ImageActorTestApp.kt index 48310648..94d7b0b0 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ImageActorTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ImageActorTestApp.kt @@ -36,7 +36,7 @@ open class ImageActorTestApp( val message = ui.newTask() try { val actor = getSettings(session, user)?.actor ?: actor - message.echo(renderMarkdown(userMessage)) + message.echo(renderMarkdown(userMessage, ui=ui)) val response = actor.answer( listOf(userMessage), api = api) message.verbose(response.text) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ParsedActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ParsedActorTestApp.kt index f9114f35..3a207db7 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ParsedActorTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/ParsedActorTestApp.kt @@ -29,7 +29,7 @@ open class ParsedActorTestApp( (api as ClientManager.MonitoredClient).budget = 2.00 val message = ui.newTask() try { - message.echo(renderMarkdown(userMessage)) + message.echo(renderMarkdown(userMessage, ui=ui)) val response = actor.answer(listOf(userMessage), api = api) message.complete( renderMarkdown( @@ -38,7 +38,7 @@ open class ParsedActorTestApp( |``` |${JsonUtil.toJson(response.obj)} |``` - """.trimMargin().trim() + """.trimMargin().trim(), ui=ui ) ) } catch (e: Throwable) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/SimpleActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/SimpleActorTestApp.kt index ed4723ff..021ec0c4 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/SimpleActorTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/SimpleActorTestApp.kt @@ -36,9 +36,9 @@ open class SimpleActorTestApp( val message = ui.newTask() try { val actor = getSettings(session, user)?.actor ?: actor - message.echo(renderMarkdown(userMessage)) + message.echo(renderMarkdown(userMessage, ui=ui)) val response = actor.answer(listOf(userMessage), api = api) - message.complete(renderMarkdown(response)) + message.complete(renderMarkdown(response, ui=ui)) } catch (e: Throwable) { log.warn("Error", e) message.error(ui, e) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/MarkdownUtil.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/MarkdownUtil.kt index 8c2976b8..7ebfcf3f 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/MarkdownUtil.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/MarkdownUtil.kt @@ -1,5 +1,7 @@ package com.simiacryptus.skyenet.webui.util +import com.simiacryptus.skyenet.AgentPatterns.displayMapInTabs +import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.vladsch.flexmark.ext.tables.TablesExtension import com.vladsch.flexmark.html.HtmlRenderer import com.vladsch.flexmark.parser.Parser @@ -7,7 +9,12 @@ import com.vladsch.flexmark.util.data.MutableDataSet import org.intellij.lang.annotations.Language object MarkdownUtil { - fun renderMarkdown(markdown: String, options: MutableDataSet = defaultOptions(), tabs : Boolean = true): String { + fun renderMarkdown( + markdown: String, + options: MutableDataSet = defaultOptions(), + tabs : Boolean = true, + ui: ApplicationInterface? = null, + ): String { if (markdown.isBlank()) return "" val parser = Parser.builder(options).build() val renderer = HtmlRenderer.builder(options).build() @@ -29,19 +36,13 @@ object MarkdownUtil { |""".trimMargin() ) //language=HTML - return if(tabs) """ - |
- |
- | - | - | - |
- |
$htmlContent
- |
- |
${markdown.replace(Regex("<"), "<").replace(Regex(">"), ">")}
- |
- |
- """.trimMargin() else htmlContent + return if(tabs) { + displayMapInTabs(mapOf( + "HTML" to htmlContent, + "Markdown" to """
${markdown.replace(Regex("<"), "<").replace(Regex(">"), ">")}
""", + "Hide" to "", + ), ui = ui) + } else htmlContent } private fun defaultOptions(): MutableDataSet { diff --git a/webui/src/main/resources/application/main.js b/webui/src/main/resources/application/main.js index 2357e416..d958a00a 100644 --- a/webui/src/main/resources/application/main.js +++ b/webui/src/main/resources/application/main.js @@ -181,7 +181,7 @@ function onWebSocketText(event) { substituteMessages(messageId, messageDiv); } }); - if(messageDivs.length == 0) { + if(messageDivs.length == 0 && !messageId.startsWith("z")) { messageDiv = document.createElement('div'); messageDiv.className = 'message message-container'; // Add the message-container class messageDiv.id = messageId; @@ -209,12 +209,36 @@ function onWebSocketText(event) { } } if (messagesDiv) messagesDiv.scrollTop = messagesDiv.scrollHeight; - if (typeof Prism !== 'undefined') Prism.highlightAll(); - refreshVerbose(); - refreshReplyForms() - if (typeof mermaid !== 'undefined') mermaid.run(); - updateTabs(); - applyToAllSvg(); + try{ + if (typeof Prism !== 'undefined') Prism.highlightAll(); + } catch (e) { + console.log("Error highlighting code: " + e); + } + try { + refreshVerbose(); + } catch (e) { + console.log("Error refreshing verbose: " + e); + } + try { + refreshReplyForms() + } catch (e) { + console.log("Error refreshing reply forms: " + e); + } + try { + if (typeof mermaid !== 'undefined') mermaid.run(); + } catch (e) { + console.log("Error running mermaid: " + e); + } + try { + updateTabs(); + } catch (e) { + console.log("Error updating tabs: " + e); + } + try { + applyToAllSvg(); + } catch (e) { + console.log("Error applying SVG pan zoom: " + e); + } } function updateTabs() {