From fcb98a23cf60c8c1f3452b8bcf7533cd7958f4b3 Mon Sep 17 00:00:00 2001 From: Andrew Charneski Date: Sat, 14 Dec 2024 14:36:48 -0500 Subject: [PATCH] strings --- .../skyenet/core/actors/CodingActor.kt | 39 +-- .../skyenet/core/actors/LargeOutputActor.kt | 235 ++++++++-------- .../skyenet/core/actors/ParsedActor.kt | 24 +- .../skyenet/kotlin/KotlinInterpreter.kt | 17 +- .../skyenet/apps/code/CodingAgent.kt | 171 ++++------- .../skyenet/apps/code/ShellToolAgent.kt | 185 ++++-------- .../skyenet/apps/general/CmdPatchApp.kt | 9 +- .../skyenet/apps/general/CommandPatchApp.kt | 9 +- .../skyenet/apps/general/PatchApp.kt | 171 +++++------ .../skyenet/apps/general/WebDevApp.kt | 118 ++++---- .../skyenet/apps/meta/CodingActorDesigner.kt | 109 ++++--- .../skyenet/apps/meta/FlowStepDesigner.kt | 134 ++++----- .../skyenet/apps/meta/ImageActorDesigner.kt | 73 +++-- .../skyenet/apps/meta/MetaAgentApp.kt | 265 ++++++++---------- .../skyenet/apps/meta/ParsedActorDesigner.kt | 103 ++++--- .../skyenet/apps/meta/SimpleActorDesigner.kt | 4 +- .../skyenet/apps/parse/CodeParsingModel.kt | 14 +- .../skyenet/apps/parse/DocumentParserApp.kt | 71 ++--- .../apps/parse/DocumentParsingModel.kt | 12 +- .../skyenet/apps/parse/LogPatternGenerator.kt | 2 +- .../skyenet/apps/plan/AbstractTask.kt | 6 +- .../skyenet/apps/plan/PlanCoordinator.kt | 46 ++- .../skyenet/apps/plan/PlanSettings.kt | 60 ++-- .../skyenet/apps/plan/PlanUtil.kt | 7 +- .../simiacryptus/skyenet/apps/plan/Planner.kt | 11 +- .../apps/plan/tools/CommandAutoFixTask.kt | 2 +- .../apps/plan/tools/CommandSessionTask.kt | 17 +- .../apps/plan/tools/GitHubSearchTask.kt | 14 +- .../apps/plan/tools/GoogleSearchTask.kt | 8 +- .../skyenet/apps/plan/tools/PlanningTask.kt | 24 +- .../apps/plan/tools/RunShellCommandTask.kt | 48 ++-- .../skyenet/apps/plan/tools/SearchTask.kt | 12 +- .../apps/plan/tools/SeleniumSessionTask.kt | 72 +++-- .../plan/tools/WebFetchAndTransformTask.kt | 8 +- .../apps/plan/tools/file/AbstractFileTask.kt | 8 +- .../plan/tools/file/CodeOptimizationTask.kt | 52 ++-- .../apps/plan/tools/file/CodeReviewTask.kt | 12 +- .../apps/plan/tools/file/DocumentationTask.kt | 40 ++- .../plan/tools/file/FileModificationTask.kt | 112 ++++---- .../apps/plan/tools/file/InquiryTask.kt | 31 +- .../tools/file/PerformanceAnalysisTask.kt | 40 ++- .../apps/plan/tools/file/RefactorTask.kt | 12 +- .../apps/plan/tools/file/SecurityAuditTask.kt | 12 +- .../tools/knowledge/WebSearchAndIndexTask.kt | 10 +- .../skyenet/interpreter/ProcessInterpreter.kt | 14 +- .../simiacryptus/skyenet/util/MarkdownUtil.kt | 20 +- .../webui/servlet/CancelThreadsServlet.kt | 31 +- .../webui/servlet/DeleteSessionServlet.kt | 28 +- .../skyenet/webui/servlet/FileServlet.kt | 2 +- .../webui/servlet/SessionSettingsServlet.kt | 34 ++- .../webui/servlet/SessionShareServlet.kt | 66 ++--- .../webui/servlet/SessionThreadsServlet.kt | 185 ++++++------ .../webui/servlet/UserSettingsServlet.kt | 52 ++-- .../skyenet/webui/session/SessionTask.kt | 48 +--- .../skyenet/webui/test/CodingActorTestApp.kt | 24 +- .../skyenet/webui/test/FilePatchTestApp.kt | 26 +- .../skyenet/webui/test/ParsedActorTestApp.kt | 7 +- .../simiacryptus/diff/DiffMatchPatchTest.kt | 126 +++++++++ .../diff/IterativePatchUtilTest.kt | 135 +++------ 59 files changed, 1444 insertions(+), 1783 deletions(-) create mode 100644 webui/src/test/kotlin/com/simiacryptus/diff/DiffMatchPatchTest.kt 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 84cead90..91df7b41 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 @@ -106,10 +106,7 @@ ${details ?: ""} get() = this.symbols.map { (name, utilityObj) -> val describe = this.describer.describe(utilityObj.javaClass) log.info("Describing $name (${utilityObj.javaClass}) in ${describe.length} characters") - """ - $name: - ${describe.indent(" ")} - """.trimMargin().trim() + "$name:\n ${describe.indent(" ")}" }.joinToString("\n") @@ -163,8 +160,8 @@ ${details ?: ""} val blocks = extractTextBlocks(respondWithCode) val renderedResponse = getRenderedResponse(blocks) val codedInstruction = codeInterceptor(getCode(language, blocks)) - log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin()) - log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin()) + log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}") + log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}") result = CodeResultImpl( *messages, input = input, @@ -254,8 +251,8 @@ ${details ?: ""} val codeBlocks = extractTextBlocks(chat(api, request, model)) val renderedResponse = getRenderedResponse(codeBlocks) val codedInstruction = codeInterceptor(getCode(language, codeBlocks)) - log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin()) - log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin()) + log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}") + log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}") var workingCode = codedInstruction var workingRenderedResponse = renderedResponse for (fixAttempt in 0..input.fixIterations) { @@ -286,16 +283,8 @@ ${TT} val codeBlocks = extractTextBlocks(respondWithCode) workingRenderedResponse = getRenderedResponse(codeBlocks) workingCode = codeInterceptor(getCode(language, codeBlocks)) - log.debug( - "Response: \n\t${ - workingRenderedResponse.replace( - "\n", - "\n\t", - false - ) - }".trimMargin() - ) - log.debug("New Code: \n\t${workingCode.replace("\n", "\n\t", false)}".trimMargin()) + log.debug("Response: \n\t" + workingRenderedResponse.replace("\n", "\n\t", false)) + log.debug("New Code: \n\t${workingCode.replace("\n", "\n\t", false)}") } } } catch (ex: FailedToImplementException) { @@ -424,7 +413,7 @@ Correct the code and try again. if (textSegments.size == 1) return textSegments.joinToString("\n") { it.second } return textSegments.joinToString("\n") { if (it.first.lowercase() == "code" || it.first.lowercase() == language.lowercase()) { - it.second.trimMargin().trim() + it.second } else { "" } @@ -487,13 +476,11 @@ Correct the code and try again. } fun errorMessage(ex: ScriptException, code: String) = try { - """ - |${TT}text - |${ex.message ?: ""} at line ${ex.lineNumber} column ${ex.columnNumber} - | ${if (ex.lineNumber > 0) code.split("\n")[ex.lineNumber - 1] else ""} - | ${if (ex.columnNumber > 0) " ".repeat(ex.columnNumber - 1) + "^" else ""} - |${TT} - """.trimMargin().trim() + "${TT}text\n${ex.message ?: ""} at line ${ex.lineNumber} column ${ex.columnNumber}\n ${if (ex.lineNumber > 0) code.split("\n")[ex.lineNumber - 1] else ""}\n ${ + if (ex.columnNumber > 0) " ".repeat( + ex.columnNumber - 1 + ) + "^" else "" + }\n${TT}".trim() } catch (_: Exception) { ex.message ?: "" } diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/LargeOutputActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/LargeOutputActor.kt index 0c802bbd..70a98af5 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/LargeOutputActor.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/LargeOutputActor.kt @@ -1,10 +1,13 @@ package com.simiacryptus.skyenet.core.actors +import com.google.common.base.Strings.commonPrefix import com.simiacryptus.jopenai.API import com.simiacryptus.jopenai.models.ApiModel +import com.simiacryptus.jopenai.models.ApiModel.Role import com.simiacryptus.jopenai.models.ChatModel import com.simiacryptus.jopenai.models.OpenAIModels import com.simiacryptus.jopenai.models.TextModel +import com.simiacryptus.jopenai.util.ClientUtil.toChatMessage import com.simiacryptus.jopenai.util.ClientUtil.toContentList /** @@ -13,135 +16,127 @@ import com.simiacryptus.jopenai.util.ClientUtil.toContentList * then recursively expands the result by searching for the pattern and making additional LLM calls. */ class LargeOutputActor( - prompt: String = """ - When generating large responses, please: - 1. Break down the content into logical sections - 2. Use named ellipsis markers like '...sectionName...' to indicate where content needs expansion - 3. Keep each section focused and concise - 4. Use descriptive section names that reflect the content - - ## Example format: - - ```markdown - # Topic Title - ## Overview - Here's an overview of the topic ...introduction... - ## Main Points - The first important aspect is ...mainPoints... - ## Technical Details - For technical details, ...technicalDetails... - ## Conclusion - To conclude, ...conclusion... - ``` - - Note: Each '...sectionName...' will be expanded in subsequent iterations. + prompt: String = """ + You are a long-form content writer. You have been tasked with writing a comprehensive guide on a topic. + 1. Break down the content into logical sections using markdown formatting and headers. + 2. To support large content generation, use markers to indicate where content needs expansion. + 3. Expansion markers should use a line formatted like '...sectionName...' to indicate where detailed content should be inserted. + 4. Use descriptive and unique section names that reflect the content expected in that section. + 5. For the initial iteration, provide a high level document structure with a few expansion markers. Each '...sectionName...' will be expanded in subsequent iterations. """.trimIndent(), - name: String? = null, - model: TextModel = OpenAIModels.GPT4o, - temperature: Double = 0.3, - private val maxIterations: Int = 5, - private val namedEllipsisPattern: Regex = Regex("""\.\.\.(?[\w\s-]+?)\.\.\.""") + name: String? = null, + model: TextModel = OpenAIModels.GPT4o, + temperature: Double = 0.3, private val maxIterations: Int = 3, private val namedEllipsisPattern: Regex = Regex("""\.\.\.(?[\w\s-_]+?)\.\.\.""") ) : BaseActor, String>( - prompt = prompt, - name = name, - model = model, - temperature = temperature + prompt = prompt, name = name, model = model, temperature = temperature ) { - override fun chatMessages(questions: List): Array { - val systemMessage = ApiModel.ChatMessage( - role = ApiModel.Role.system, - content = prompt.toContentList() + override fun chatMessages(questions: List): Array { + val systemMessage = ApiModel.ChatMessage( + role = Role.system, content = prompt.toContentList() + ) + val userMessages = questions.map { + ApiModel.ChatMessage( + role = Role.user, content = it.toContentList() + ) + } + return arrayOf(systemMessage) + userMessages + } + + override fun respond(input: List, api: API, vararg messages: ApiModel.ChatMessage): String { + var accumulatedResponse = "" + var iterations = 0 + while (iterations < maxIterations) { + if (accumulatedResponse.isEmpty()) { + accumulatedResponse = response(*messages, api = api).choices.first().message?.content?.trim() ?: throw RuntimeException("No response from LLM") + } + val matches = namedEllipsisPattern.findAll(accumulatedResponse).toMutableList() + if (matches.isEmpty()) break + val pairs = matches.mapNotNull { matchResult -> + val nextSection = matchResult.groups["sectionName"]?.value ?: return@mapNotNull null + val contextLines = 100 + val contextChars = 10000 + Pair( + matchResult, response( + *(listOf( + """ + You are a long-form content writer. You have been tasked with writing a comprehensive guide on a topic by filling in a detail section. + 1. Break down the content into logical sections using markdown formatting and headers. + 2. To support large content generation, use markers to indicate where content needs expansion. + 3. Expansion markers should use a line formatted like '...sectionName...' to indicate where detailed content should be inserted. + 4. Use descriptive and unique section names that reflect the content expected in that section. + """.trimIndent().toChatMessage(Role.system) + ) + messages.toList().drop(1) + listOf( + ApiModel.ChatMessage( + role = Role.user, content = (""" + Previous context: + + ``` + """.trimIndent() + accumulatedResponse.substring(0, matchResult.range.first).lines().takeLast(contextLines).joinToString { " $it" }.takeLast(contextChars) + """ + ``` + + Continue the section '""".trimIndent() + nextSection + """' + Make sure the response flows naturally with the existing content. + It should end so that it matches the next section, provided below: + + ``` + """.trimIndent() + accumulatedResponse.substring(matchResult.range.last).lines().take(contextLines).joinToString { " $it" }.take(contextChars) + """ + ``` + """.trimIndent()).toContentList() + ) + )).toTypedArray(), api = api + ) ) - val userMessages = questions.map { - ApiModel.ChatMessage( - role = ApiModel.Role.user, - content = it.toContentList() - ) + } + accumulatedResponse = pairs.reversed().fold(accumulatedResponse) { acc, (match, response) -> + val original = response.choices.first().message?.content?.trim() ?: "" + var replacement = original + if (replacement.isEmpty()) return acc + //val replaced = acc.substring(match.range) + if (replacement.startsWith("```")) { + replacement = replacement.lines().drop(1).reversed().dropWhile { !it.startsWith("```") }.drop(1).reversed().joinToString("\n") } - return arrayOf(systemMessage) + userMessages + val prefix = acc.substring(0, match.range.first) + val suffix = acc.substring(match.range.last) + val commonPrefix = commonPrefix(prefix, replacement) + if (commonPrefix.isNotBlank() && commonPrefix.contains('\n')) replacement = replacement.substring(commonPrefix.length) + val largestCommonSubstring = largestCommonSubstring(replacement, suffix) + if (largestCommonSubstring.isNotBlank()) replacement = replacement.substring(0, replacement.indexOf(largestCommonSubstring)) + val replaceRange = acc.replaceRange(match.range, replacement) + replaceRange + } + iterations++ } + return accumulatedResponse + } - override fun respond(input: List, api: API, vararg messages: ApiModel.ChatMessage): String { - var accumulatedResponse = "" - var currentMessages = messages.toList() - var iterations = 0 - var previousContext = "" - var processedSections = mutableSetOf() - - while (iterations < maxIterations) { - val response = response(*currentMessages.toTypedArray(), api = api).choices.first().message?.content - ?: throw RuntimeException("No response from LLM") - - // Replace the ellipsis in the accumulated response with the new content - if (previousContext.isNotEmpty()) { - val lastEllipsis = namedEllipsisPattern.find(accumulatedResponse) - if (lastEllipsis != null) { - accumulatedResponse = accumulatedResponse.replaceRange( - lastEllipsis.range.first, - lastEllipsis.range.last + 1, - response.trim() - ) - } - } else { - accumulatedResponse = response.trim() - } - - val matches = namedEllipsisPattern.findAll(response) - .mapNotNull { it.groups["sectionName"]?.value } - .filter { it !in processedSections } - .toList() - - if (matches.isNotEmpty()) { - val nextSection = matches.first() - processedSections.add(nextSection) + override fun withModel(model: ChatModel): LargeOutputActor { + return LargeOutputActor( + prompt = this.prompt, + name = this.name, + model = model, + temperature = this.temperature, + maxIterations = this.maxIterations, + namedEllipsisPattern = this.namedEllipsisPattern + ) + } +} - // Identify the pattern after the ellipsis to continue - val continuationRequest = """ - |Previous context: - |$accumulatedResponse - | - |Continue the section '$nextSection' by expanding the ellipsis. - |Make sure the response flows naturally with the existing content. - |Keep the response focused and avoid creating new ellipsis markers. - """.trimMargin() - currentMessages = listOf( - ApiModel.ChatMessage( - role = ApiModel.Role.user, - content = continuationRequest.toContentList() - ) - ) - previousContext = accumulatedResponse - iterations++ - } else { - break - } +fun largestCommonSubstring(a: String, b: String): String { + val lengths = Array(a.length + 1) { IntArray(b.length + 1) } + var z = 0 + var ret = "" + for (i in 0 until a.length) { + for (j in 0 until b.length) { + if (a[i] == b[j]) { + lengths[i + 1][j + 1] = lengths[i][j] + 1 + val len = lengths[i + 1][j + 1] + if (len > z) { + z = len + ret = a.substring(i - z + 1, i + 1) } - - if (iterations == maxIterations && namedEllipsisPattern.containsMatchIn(accumulatedResponse)) { - throw RuntimeException(""" - |Maximum iterations ($maxIterations) reached. Output may be incomplete. - |Processed sections: ${processedSections.joinToString(", ")} - |Remaining ellipsis markers: ${ - namedEllipsisPattern.findAll(accumulatedResponse) - .mapNotNull { it.groups["sectionName"]?.value } - .joinToString(", ") - } - |Current length: ${accumulatedResponse.length} - """.trimMargin()) - } - - return accumulatedResponse - } - - override fun withModel(model: ChatModel): LargeOutputActor { - return LargeOutputActor( - prompt = this.prompt, - name = this.name, - model = model, - temperature = this.temperature, - maxIterations = this.maxIterations, - namedEllipsisPattern = this.namedEllipsisPattern - ) + } } + } + return ret } \ No newline at end of file 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 0b651022..624ff155 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 @@ -66,19 +66,17 @@ open class ParsedActor( val describe = resultClass?.let { describer.describe(it) } ?: "" val exceptions = mutableListOf() val prompt = """ - |Parse the user's message into a json object described by: - | - |```yaml - |${describe.replace("\n", "\n ")} - |``` - | - |This is an example output: - |```json - |${JsonUtil.toJson(exampleInstance!!)/*.indent(" ")*/} - |``` - |${promptSuffix?.let { "\n$it" } ?: ""} - | - """.trimMargin() + Parse the user's message into a json object described by: + + ```yaml + """.trimIndent() + describe.replace("\n", "\n ") + """ + ``` + + This is an example output: + ```json + """ + JsonUtil.toJson(exampleInstance!!) + """ + ``` + """.trimIndent() + (promptSuffix?.let { "\n$it" } ?: "") for (i in 0 until deserializerRetries) { try { val content = (api as ChatClient).chat( diff --git a/kotlin/src/main/kotlin/com/simiacryptus/skyenet/kotlin/KotlinInterpreter.kt b/kotlin/src/main/kotlin/com/simiacryptus/skyenet/kotlin/KotlinInterpreter.kt index 0f8f08f9..a1c5267f 100644 --- a/kotlin/src/main/kotlin/com/simiacryptus/skyenet/kotlin/KotlinInterpreter.kt +++ b/kotlin/src/main/kotlin/com/simiacryptus/skyenet/kotlin/KotlinInterpreter.kt @@ -75,10 +75,7 @@ open class KotlinInterpreter( override fun run(code: String): Any? { val wrappedCode = wrapCode(code) log.debug( - """ - |Running: - | ${wrappedCode.trimIndent().replace("\n", "\n\t")} - |""".trimMargin().trim() + "Running:\n ${wrappedCode.trimIndent().replace("\n", "\n\t")}" ) val bindings: Bindings? val compile: CompiledScript @@ -143,13 +140,11 @@ open class KotlinInterpreter( line: Int, column: Int, message: String - ) = """ - |```text - |$message at line ${line} column ${column} - | ${if (line < 0) "" else code.split("\n")[line - 1]} - | ${if (column < 0) "" else " ".repeat(column - 1) + "^"} - |``` - """.trimMargin().trim() + ) = "```text\n$message at line ${line} column ${column}\n ${if (line < 0) "" else code.split("\n")[line - 1]}\n ${ + if (column < 0) "" else " ".repeat( + column - 1 + ) + "^" + }\n```".trim() // TODO: Make this threadlocal with wrapper methods var classLoader: ClassLoader? = KotlinInterpreter::class.java.classLoader diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/CodingAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/CodingAgent.kt index e7336721..4fec2b9f 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/CodingAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/CodingAgent.kt @@ -38,11 +38,7 @@ open class CodingAgent( private val mainTask: SessionTask, val actorMap: Map = mapOf( ActorTypes.CodingActor to CodingActor( - interpreter, - symbols = symbols, - temperature = temperature, - details = details, - model = model + interpreter, symbols = symbols, temperature = temperature, details = details, model = model ) ), ) : ActorSystem(actorMap.map { it.key.name to it.value }.toMap(), dataStorage, user, session) { @@ -56,9 +52,7 @@ open class CodingAgent( open val canPlay by lazy { ApplicationServices.authorizationManager.isAuthorized( - this::class.java, - user, - OperationType.Execute + this::class.java, user, OperationType.Execute ) } @@ -95,8 +89,7 @@ open class CodingAgent( } } - open fun codeRequest(messages: List>) = - CodingActor.CodeRequest(messages) + open fun codeRequest(messages: List>) = CodingActor.CodeRequest(messages) fun displayCode( task: SessionTask, @@ -135,22 +128,17 @@ open class CodingAgent( } fun append( - codeRequest: CodingActor.CodeRequest, - response: CodeResult + codeRequest: CodingActor.CodeRequest, response: CodeResult ) = codeRequest( - messages = codeRequest.messages + - listOf( - response.code to ApiModel.Role.assistant, - ).filter { it.first.isNotBlank() } - ) + messages = codeRequest.messages + listOf( + response.code to ApiModel.Role.assistant, + ).filter { it.first.isNotBlank() }) fun displayCode( - task: SessionTask, - response: CodeResult + task: SessionTask, response: CodeResult ) { task.hideable( - ui, - renderMarkdown( + ui, renderMarkdown( response.renderedResponse ?: //language=Markdown "```${actor.language.lowercase(Locale.getDefault())}\n${response.code.trim()}\n```", ui = ui @@ -159,19 +147,16 @@ open class CodingAgent( } open fun displayFeedback( - task: SessionTask, - request: CodingActor.CodeRequest, - response: CodeResult + task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult ) { val formText = StringBuilder() var formHandle: StringBuilder? = null formHandle = task.add( - """ - |
- |${if (!canPlay) "" else playButton(task, request, response, formText) { formHandle!! }} - |
- |${reviseMsg(task, request, response, formText) { formHandle!! }} - """.trimMargin(), additionalClasses = "reply-message" + "
\n${ + if (!canPlay) "" else playButton( + task, request, response, formText + ) { formHandle!! } + }\n
\n${reviseMsg(task, request, response, formText) { formHandle!! }}", additionalClasses = "reply-message" ) formText.append(formHandle.toString()) formHandle.toString() @@ -180,11 +165,7 @@ open class CodingAgent( protected fun reviseMsg( - task: SessionTask, - request: CodingActor.CodeRequest, - response: CodeResult, - formText: StringBuilder, - formHandle: () -> StringBuilder + task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult, formText: StringBuilder, formHandle: () -> StringBuilder ) = ui.textInput { feedback -> responseAction(task, "Revising...", formHandle(), formText) { feedback(task, feedback, request, response) @@ -192,31 +173,19 @@ open class CodingAgent( } protected fun regenButton( - task: SessionTask, - request: CodingActor.CodeRequest, - formText: StringBuilder, - formHandle: () -> StringBuilder + task: SessionTask, request: CodingActor.CodeRequest, formText: StringBuilder, formHandle: () -> StringBuilder ) = "" protected fun playButton( - task: SessionTask, - request: CodingActor.CodeRequest, - response: CodeResult, - formText: StringBuilder, - formHandle: () -> StringBuilder - ) = if (!canPlay) "" else - ui.hrefLink("▶", "href-link play-button") { - responseAction(task, "Running...", formHandle(), formText) { - execute(task, response, request) - } + task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult, formText: StringBuilder, formHandle: () -> StringBuilder + ) = if (!canPlay) "" else ui.hrefLink("▶", "href-link play-button") { + responseAction(task, "Running...", formHandle(), formText) { + execute(task, response, request) } + } protected open fun responseAction( - task: SessionTask, - message: String, - formHandle: StringBuilder?, - formText: StringBuilder, - fn: () -> Unit = {} + task: SessionTask, message: String, formHandle: StringBuilder?, formText: StringBuilder, fn: () -> Unit = {} ) { formHandle?.clear() val header = task.header(message) @@ -229,9 +198,7 @@ open class CodingAgent( } protected open fun revertButton( - task: SessionTask, - formHandle: StringBuilder?, - formText: StringBuilder + task: SessionTask, formHandle: StringBuilder?, formText: StringBuilder ): StringBuilder? { var revertButton: StringBuilder? = null revertButton = task.complete(ui.hrefLink("↩", "href-link regen-button") { @@ -243,20 +210,16 @@ open class CodingAgent( } protected open fun feedback( - task: SessionTask, - feedback: String, - request: CodingActor.CodeRequest, - response: CodeResult + task: SessionTask, feedback: String, request: CodingActor.CodeRequest, response: CodeResult ) { try { task.echo(renderMarkdown(feedback, ui = ui)) - start(codeRequest = codeRequest( - messages = request.messages + - listOf( - response.code to ApiModel.Role.assistant, - feedback to ApiModel.Role.user, - ).filter { it.first.isNotBlank() }.map { it.first to it.second } - ), task = task) + start( + codeRequest = codeRequest( + messages = request.messages + listOf( + response.code to ApiModel.Role.assistant, + feedback to ApiModel.Role.user, + ).filter { it.first.isNotBlank() }.map { it.first to it.second }), task = task) } catch (e: Throwable) { log.warn("Error", e) task.error(ui, e) @@ -270,79 +233,41 @@ open class CodingAgent( ) { try { val result = execute(task, response) - displayFeedback(task, codeRequest( - messages = request.messages + - listOf( - "Running...\n\n$result" to ApiModel.Role.assistant, - ).filter { it.first.isNotBlank() } - ), response) + displayFeedback( + task, codeRequest( + messages = request.messages + listOf( + "Running...\n\n$result" to ApiModel.Role.assistant, + ).filter { it.first.isNotBlank() }), response) } catch (e: Throwable) { handleExecutionError(e, task, request, response) } } protected open fun handleExecutionError( - e: Throwable, - task: SessionTask, - request: CodingActor.CodeRequest, - response: CodeResult + e: Throwable, task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult ) { val message = when { e is ValidatedObject.ValidationError -> renderMarkdown(e.message ?: "", ui = ui) - e is CodingActor.FailedToImplementException -> renderMarkdown( - """ - |**Failed to Implement** - | - |${e.message} - | - |""".trimMargin(), ui = ui - ) - - else -> renderMarkdown( - """ - |**Error `${e.javaClass.name}`** - | - |```text - |${e.stackTraceToString()/*.indent(" ")*/} - |``` - |""".trimMargin(), ui = ui - ) + e is CodingActor.FailedToImplementException -> renderMarkdown("**Failed to Implement** \n\n${e.message}\n\n", ui = ui) + else -> renderMarkdown("**Error `${e.javaClass.name}`**\n\n```text\n${e.stackTraceToString()}\n```\n", ui = ui) } task.add(message, true, "div", "error") - displayCode(task, CodingActor.CodeRequest( - messages = request.messages + - listOf( - response.code to ApiModel.Role.assistant, - message to ApiModel.Role.system, - ).filter { it.first.isNotBlank() } - )) + displayCode( + task, CodingActor.CodeRequest( + messages = request.messages + listOf( + response.code to ApiModel.Role.assistant, + message to ApiModel.Role.system, + ).filter { it.first.isNotBlank() })) } fun execute( - task: SessionTask, - response: CodeResult + task: SessionTask, response: CodeResult ): String { val resultValue = response.result.resultValue val resultOutput = response.result.resultOutput val result = when { - resultValue.isBlank() || resultValue.trim().lowercase() == "null" -> """ - |# Output - |```text - |${resultOutput.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} - |``` - """.trimMargin() - - else -> """ - |# Result - |``` - |${resultValue.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} - |``` - | - |# Output - |```text - |${resultOutput.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} - |``` - """.trimMargin() + resultValue.isBlank() || resultValue.trim().lowercase() == "null" -> "# Output\n```text\n$resultOutput\n```" + else -> "# Result\n```\n$resultValue\n```\n\n# Output\n```text\n$resultOutput\n```" } task.add(renderMarkdown(result, ui = ui)) return result diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/ShellToolAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/ShellToolAgent.kt index 8802fb5a..0ab7c4bf 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/ShellToolAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/code/ShellToolAgent.kt @@ -11,7 +11,8 @@ import com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.sortCode import com.simiacryptus.skyenet.core.actors.ParsedActor import com.simiacryptus.skyenet.core.actors.SimpleActor -import com.simiacryptus.skyenet.core.platform.* +import com.simiacryptus.skyenet.core.platform.ApplicationServices +import com.simiacryptus.skyenet.core.platform.Session import com.simiacryptus.skyenet.core.platform.model.AuthorizationInterface import com.simiacryptus.skyenet.core.platform.model.StorageInterface import com.simiacryptus.skyenet.core.platform.model.User @@ -35,11 +36,7 @@ import java.io.File import kotlin.reflect.KClass private val String.escapeQuotedString: String - get() = replace("\\", "\\\\") - .replace("\"", "\\\"") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("$", "\\$") + get() = replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("$", "\\$") abstract class ShellToolAgent( api: API, @@ -54,27 +51,12 @@ abstract class ShellToolAgent( model: ChatModel, actorMap: Map = mapOf( ActorTypes.CodingActor to CodingActor( - interpreter, - symbols = symbols, - temperature = temperature, - details = details, - model = model + interpreter, symbols = symbols, temperature = temperature, details = details, model = model ) ), mainTask: SessionTask = ui.newTask(), ) : CodingAgent( - api, - dataStorage, - session, - user, - ui, - interpreter, - symbols, - temperature, - details, - model, - mainTask, - actorMap + api, dataStorage, session, user, ui, interpreter, symbols, temperature, details, model, mainTask, actorMap ) { @@ -82,14 +64,17 @@ abstract class ShellToolAgent( val formText = StringBuilder() var formHandle: StringBuilder? = null formHandle = task.add( - """ - |
- |${if (!canPlay) "" else playButton(task, request, response, formText) { formHandle!! }} - |${super.regenButton(task, request, formText) { formHandle!! }} - |${createToolButton(task, request, response, formText) { formHandle!! }} - |
- |${super.reviseMsg(task, request, response, formText) { formHandle!! }} - """.trimMargin(), additionalClasses = "reply-message" + "
\n" + (if (!canPlay) "" else playButton( + task, + request, + response, + formText + ) { formHandle!! }) + "\n" + super.regenButton(task, request, formText) { formHandle!! } + "\n" + createToolButton( + task, + request, + response, + formText + ) { formHandle!! } + "\n
\n" + super.reviseMsg(task, request, response, formText) { formHandle!! }, additionalClasses = "reply-message" ) formText.append(formHandle.toString()) formHandle.toString() @@ -99,11 +84,7 @@ abstract class ShellToolAgent( private var lastResult: String? = null private fun createToolButton( - task: SessionTask, - request: CodingActor.CodeRequest, - response: CodeResult, - formText: StringBuilder, - formHandle: () -> StringBuilder + task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult, formText: StringBuilder, formHandle: () -> StringBuilder ) = ui.hrefLink("\uD83D\uDCE4", "href-link regen-button") { val task = ui.newTask() responseAction(task, "Exporting...", formHandle(), formText) { @@ -157,18 +138,15 @@ abstract class ShellToolAgent( ) displayCodeFeedback( task, parsedActor(), request.copy( - messages = messages, - codePrefix = codePrefix + messages = messages, codePrefix = codePrefix ) ) { parsedCode -> displayCodeFeedback( task, servletActor(), request.copy( messages = listOf( (codePrefix + "\n\n" + parsedCode) to ApiModel.Role.assistant, - "Reprocess this code prototype into a servlet. " + - "The last line should instantiate the new servlet class and return it via the returnBuffer collection." to ApiModel.Role.user - ), - codePrefix = schemaCode + "Reprocess this code prototype into a servlet. " + "The last line should instantiate the new servlet class and return it via the returnBuffer collection." to ApiModel.Role.user + ), codePrefix = schemaCode ) ) { servletHandler -> val servletImpl = (schemaCode + "\n\n" + servletHandler).sortCode() @@ -191,8 +169,7 @@ abstract class ShellToolAgent( "html2", "-o", File( - dataStorage.getSessionDir(user, session), - "openapi/html2" + dataStorage.getSessionDir(user, session), "openapi/html2" ).apply { mkdirs() }.absolutePath, ) ) @@ -205,38 +182,29 @@ abstract class ShellToolAgent( |${e.warnings.joinToString("\n") { "WARN:" + it.toString() }} """.trimIndent() task.hideable( - ui, - renderMarkdown( - "```\n${error.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", - ui = ui + ui, renderMarkdown( + "```\n${error}\n```", ui = ui ) ) openAPI = openAPIParsedActor().answer( listOf( - servletImpl, - JsonUtil.toJson(openAPI), - error + servletImpl, JsonUtil.toJson(openAPI), error ), api ).obj.let { openApi -> 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```", - ui = ui + ui, renderMarkdown( + "```json\n${JsonUtil.toJson(openAPI)}\n```", ui = ui ) ) } } if (ApplicationServices.authorizationManager.isAuthorized( - ShellToolAgent.javaClass, - user, - AuthorizationInterface.OperationType.Admin + ShellToolAgent.javaClass, user, AuthorizationInterface.OperationType.Admin ) - ) { - /* + ) {/* ToolServlet.addTool( ToolServlet.Tool( path = openAPI.paths?.entries?.first()?.key?.removePrefix(toolsPrefix) ?: "unknown", @@ -270,32 +238,22 @@ abstract class ShellToolAgent( } private fun servletActor() = object : CodingActor( - interpreterClass = KotlinInterpreter::class, - symbols = actor.symbols + mapOf( + interpreterClass = KotlinInterpreter::class, symbols = actor.symbols + mapOf( "returnBuffer" to ServletBuffer(), "json" to JsonUtil, "req" to Request(null, null), "resp" to Response(null, null), - ), - describer = object : AbbrevWhitelistYamlDescriber( - "com.simiacryptus", - "com.github.simiacryptus" + ), describer = object : AbbrevWhitelistYamlDescriber( + "com.simiacryptus", "com.github.simiacryptus" ) { override fun describe( - rawType: Class, - stackMax: Int, - describedTypes: MutableSet + rawType: Class, stackMax: Int, describedTypes: MutableSet ): String = when (rawType) { Request::class.java -> describe(HttpServletRequest::class.java) Response::class.java -> describe(HttpServletResponse::class.java) else -> super.describe(rawType, stackMax, describedTypes) } - }, - details = actor.details, - model = actor.model, - fallbackModel = actor.fallbackModel, - temperature = actor.temperature, - runtimeSymbols = actor.runtimeSymbols + }, details = actor.details, model = actor.model, fallbackModel = actor.fallbackModel, temperature = actor.temperature, runtimeSymbols = actor.runtimeSymbols ) { override val prompt: String get() = super.prompt @@ -342,36 +300,24 @@ abstract class ShellToolAgent( onComplete: (String) -> Unit ) { task.hideable( - ui, - renderMarkdown("```kotlin\n${response.code.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui = ui) + ui, renderMarkdown("```kotlin\n${response.code.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui = ui) ) val formText = StringBuilder() var formHandle: StringBuilder? = null - formHandle = task.add( - """ - |
- |${ - super.ui.hrefLink("\uD83D\uDC4D", "href-link play-button") { + formHandle = + task.add( + "\n
\n " + super.ui.hrefLink("\uD83D\uDC4D", "href-link play-button") { super.responseAction(task, "Accepted...", formHandle!!, formText) { onComplete(response.code) } - } - } - |${ - if (!super.canPlay) "" else - ui.hrefLink("▶", "href-link play-button") { - execute(ui.newTask(), response) - } - } - |${ - super.ui.hrefLink("♻", "href-link regen-button") { + } + "\n " + (if (!super.canPlay) "" else ui.hrefLink("▶", "href-link play-button") { + execute(ui.newTask(), response) + }) + "\n " + super.ui.hrefLink("♻", "href-link regen-button") { super.responseAction(task, "Regenerating...", formHandle!!, formText) { //val task = super.ui.newTask() - val codeRequest = - request.copy(messages = request.messages.dropLastWhile { it.second == ApiModel.Role.assistant }) + val codeRequest = request.copy(messages = request.messages.dropLastWhile { it.second == ApiModel.Role.assistant }) try { - val lastUserMessage = - codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim() + val lastUserMessage = codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim() val codeResponse: CodeResult = if (lastUserMessage.startsWith("```")) { actor.CodeResultImpl( messages = actor.chatMessages(codeRequest), @@ -384,11 +330,7 @@ abstract class ShellToolAgent( } super.displayCode(task, codeResponse) displayCodeFeedback( - task, - actor, - super.append(codeRequest, codeResponse), - codeResponse, - onComplete + task, actor, super.append(codeRequest, codeResponse), codeResponse, onComplete ) } catch (e: Throwable) { log.warn("Error", e) @@ -404,25 +346,18 @@ abstract class ShellToolAgent( }) } } - } - } - |
- |${ - super.ui.textInput { feedback -> + } + "
" + super.ui.textInput { feedback -> super.responseAction(task, "Revising...", formHandle!!, formText) { //val task = super.ui.newTask() try { task.echo(renderMarkdown(feedback, ui = ui)) val codeRequest = CodingActor.CodeRequest( - messages = request.messages + - listOf( - response.code to ApiModel.Role.assistant, - feedback to ApiModel.Role.user, - ).filter { it.first.isNotBlank() }.map { it.first to it.second } - ) + messages = request.messages + listOf( + response.code to ApiModel.Role.assistant, + feedback to ApiModel.Role.user, + ).filter { it.first.isNotBlank() }.map { it.first to it.second }) try { - val lastUserMessage = - codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim() + val lastUserMessage = codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim() val codeResponse: CodeResult = if (lastUserMessage.startsWith("```")) { actor.CodeResultImpl( messages = actor.chatMessages(codeRequest), @@ -434,11 +369,7 @@ abstract class ShellToolAgent( actor.answer(codeRequest, api = super.api) } displayCodeFeedback( - task, - actor, - super.append(codeRequest, codeResponse), - codeResponse, - onComplete + task, actor, super.append(codeRequest, codeResponse), codeResponse, onComplete ) } catch (e: Throwable) { log.warn("Error", e) @@ -458,10 +389,8 @@ abstract class ShellToolAgent( task.error(ui, e) } } - } - } - """.trimMargin(), additionalClasses = "reply-message" - ) + }, additionalClasses = "reply-message" + ) formText.append(formHandle.toString()) formHandle.toString() task.complete() @@ -471,17 +400,14 @@ abstract class ShellToolAgent( class ServletBuffer : ArrayList() private fun buildTestPage( - openAPI: OpenAPI, - servletImpl: String, - task: SessionTask + openAPI: OpenAPI, servletImpl: String, task: SessionTask ) { var testPage = SimpleActor( prompt = "Given the definition for a servlet handler, create a test page that can be used to test the servlet", model = model, ).answer( listOf( - JsonUtil.toJson(openAPI), - servletImpl + JsonUtil.toJson(openAPI), servletImpl ), api ) // if ```html unwrap @@ -490,8 +416,7 @@ abstract class ShellToolAgent( task.complete( "Test Page for ${openAPI.paths?.entries?.first()?.key ?: "unknown"} Saved" ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CmdPatchApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CmdPatchApp.kt index 48329190..df72248f 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CmdPatchApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CmdPatchApp.kt @@ -68,12 +68,9 @@ class CmdPatchApp( } .joinToString("\n\n") { path -> try { - """ - |# ${path} - |${tripleTilde}${path.toString().split('.').lastOrNull()} - |${settings.workingDirectory?.resolve(path.toFile())?.readText(Charsets.UTF_8)} - |${tripleTilde} - """.trimMargin() + "# ${path}\n${tripleTilde}${path.toString().split('.').lastOrNull()}\n${ + settings.workingDirectory?.resolve(path.toFile())?.readText(Charsets.UTF_8) + }\n${tripleTilde}" } catch (e: Exception) { log.warn("Error reading file", e) "Error reading file `${path}` - ${e.message}" diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CommandPatchApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CommandPatchApp.kt index e6251b66..b6e6f513 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CommandPatchApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/CommandPatchApp.kt @@ -24,12 +24,9 @@ class CommandPatchApp( override fun codeSummary(paths: List): String = paths .filter { it.toFile().exists() } .joinToString("\n\n") { path -> - """ - |# ${settings.workingDirectory?.toPath()?.relativize(path)} - |$tripleTilde${path.toString().split('.').lastOrNull()} - |${path.toFile().readText(Charsets.UTF_8)} - |$tripleTilde - """.trimMargin() + "# ${settings.workingDirectory?.toPath()?.relativize(path)}\n$tripleTilde${path.toString().split('.').lastOrNull()}\n${ + path.toFile().readText(Charsets.UTF_8) + }\n$tripleTilde" } override fun output(task: SessionTask) = OutputResult( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/PatchApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/PatchApp.kt index 33f8dc89..01ea60a4 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/PatchApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/PatchApp.kt @@ -133,34 +133,19 @@ abstract class PatchApp( val output = output(task) if (output.exitCode == 0 && settings.exitCodeOption == "nonzero") { task.complete( - """ - |
- |
Command executed successfully
- |${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")} - |
- |""".trimMargin() + "
\n
Command executed successfully
\n${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")}\n
" ) return output } if (settings.exitCodeOption == "zero" && output.exitCode != 0) { task.complete( - """ - |
- |
Command failed
- |${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")} - |
- |""".trimMargin() + "
\n
Command failed
\n${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")}\n
" ) return output } try { task.add( - """ - |
- |
Command exit code: ${output.exitCode}
- |${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")} - |
- """.trimMargin() + "
\n
Command exit code: ${output.exitCode}
\n${MarkdownUtil.renderMarkdown("${tripleTilde}\n${output.output}\n${tripleTilde}")}\n
" ) fixAll(settings, output, task, ui, api) } catch (e: Exception) { @@ -231,33 +216,26 @@ abstract class PatchApp( ) ) ), - prompt = """ - |You are a helpful AI that helps people with coding. - | - |You will be answering questions about the following project: - | - |Project Root: ${settings.workingDirectory?.absolutePath ?: ""} - | - |Files: - |${projectSummary()} - | - |Given the response of a build/test process, identify one or more distinct errors. - |For each error: - | 1) predict the files that need to be fixed - | 2) predict related files that may be needed to debug the issue - | 3) specify a search string to find relevant files - be as specific as possible - |${if (settings.additionalInstructions.isNotBlank()) "Additional Instructions:\n ${settings.additionalInstructions}\n" else ""} - """.trimMargin(), - model = model + model = model, + prompt = (""" + You are a helpful AI that helps people with coding. + + You will be answering questions about the following project: + + Project Root: """.trimIndent() + (settings.workingDirectory?.absolutePath ?: "") + """ + + Files: + """.trimIndent() + projectSummary() + """ + + Given the response of a build/test process, identify one or more distinct errors. + For each error: + 1) predict the files that need to be fixed + 2) predict related files that may be needed to debug the issue + 3) specify a search string to find relevant files - be as specific as possible + """.trimIndent() + (if (settings.additionalInstructions.isNotBlank()) "Additional Instructions:\n ${settings.additionalInstructions}\n" else "")) ).answer( listOf( - """ - |$promptPrefix - | - |${tripleTilde} - |${output.output} - |${tripleTilde} - """.trimMargin() + "$promptPrefix\n\n${tripleTilde}\n${output.output}\n${tripleTilde}" ), api = api ) task.add( @@ -285,11 +263,7 @@ abstract class PatchApp( }?.toSet() ?: emptySet() task.verbose( MarkdownUtil.renderMarkdown( - """ - |Search results: - | - |${searchResults.joinToString("\n") { "* `$it`" }} - """.trimMargin(), tabs = false, ui = ui + "Search results:\n\n${searchResults.joinToString("\n") { "* `$it`" }}", tabs = false, ui = ui ) ) Retryable(ui, task) { content -> @@ -331,62 +305,57 @@ abstract class PatchApp( val summary = codeSummary(prunedPaths) val response = SimpleActor( prompt = """ - |You are a helpful AI that helps people with coding. - | - |You will be answering questions about the following code: - | - |$summary - | - | - |Response should use one or more code patches in diff format within ${tripleTilde}diff code blocks. - |Each diff should be preceded by a header that identifies the file being modified. - |The diff format should use + for line additions, - for line deletions. - |The diff should include 2 lines of context before and after every change. - | - |Example: - | - |Here are the patches: - | - |### src/utils/exampleUtils.js - |${tripleTilde}diff - | // Utility functions for example feature - | const b = 2; - | function exampleFunction() { - |- return b + 1; - |+ return b + 2; - | } - |${tripleTilde} - | - |### tests/exampleUtils.test.js - |${tripleTilde}diff - | // Unit tests for exampleUtils - | const assert = require('assert'); - | const { exampleFunction } = require('../src/utils/exampleUtils'); - | - | describe('exampleFunction', () => { - |- it('should return 3', () => { - |+ it('should return 4', () => { - | assert.equal(exampleFunction(), 3); - | }); - | }); - |${tripleTilde} - | - |If needed, new files can be created by using code blocks labeled with the filename in the same manner. - """.trimMargin(), + You are a helpful AI that helps people with coding. + + You will be answering questions about the following code: + + """.trimIndent() + summary + """ + + + Response should use one or more code patches in diff format within """.trimIndent() + tripleTilde + """diff code blocks. + Each diff should be preceded by a header that identifies the file being modified. + The diff format should use + for line additions, - for line deletions. + The diff should include 2 lines of context before and after every change. + + Example: + + Here are the patches: + + ### src/utils/exampleUtils.js + """.trimIndent() + tripleTilde + """diff + // Utility functions for example feature + const b = 2; + function exampleFunction() { + - return b + 1; + + return b + 2; + } + """.trimIndent() + tripleTilde + """ + + ### tests/exampleUtils.test.js + """.trimIndent() + tripleTilde + """diff + // Unit tests for exampleUtils + const assert = require('assert'); + const { exampleFunction } = require('../src/utils/exampleUtils'); + + describe('exampleFunction', () => { + - it('should return 3', () => { + + it('should return 4', () => { + assert.equal(exampleFunction(), 3); + }); + }); + """.trimIndent() + tripleTilde + """ + + If needed, new files can be created by using code blocks labeled with the filename in the same manner. + """.trimIndent(), model = model ).answer( listOf( - """ - |$promptPrefix - | - |${tripleTilde} - |${output.output} - |${tripleTilde} - | - |Focus on and Fix the Error: - | ${error.message?.replace("\n", "\n ") ?: ""} - |${if (settings.additionalInstructions.isNotBlank()) "Additional Instructions:\n ${settings.additionalInstructions}\n" else ""} - """.trimMargin() + "$promptPrefix\n\n${tripleTilde}\n${output.output}\n${tripleTilde}\n\nFocus on and Fix the Error:\n ${ + error.message?.replace( + "\n", + "\n " + ) ?: "" + }\n${if (settings.additionalInstructions.isNotBlank()) "Additional Instructions:\n ${settings.additionalInstructions}\n" else ""}" ), api = api ) var markdown = ui.socketManager?.addApplyFileDiffLinks( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/WebDevApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/WebDevApp.kt index 102aa84b..bb394b8a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/WebDevApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/general/WebDevApp.kt @@ -95,87 +95,87 @@ class WebDevAgent( // parserClass = PageResourceListParser::class.java, resultClass = ProjectSpec::class.java, prompt = """ - |Translate the user's idea into a detailed architecture for a simple web application. - | - | List all html, css, javascript, and image files to be created, and for each file: - | 1. Mark with filename tags. - | 2. Describe the public interface / interaction with other components. - | 3. Core functional requirements. - | - |Specify user interactions and how the application will respond to them. - |Identify key HTML classes and element IDs that will be used to bind the application to the HTML. - """.trimMargin(), + Translate the user's idea into a detailed architecture for a simple web application. + + List all html, css, javascript, and image files to be created, and for each file: + 1. Mark with filename tags. + 2. Describe the public interface / interaction with other components. + 3. Core functional requirements. + + Specify user interactions and how the application will respond to them. + Identify key HTML classes and element IDs that will be used to bind the application to the HTML. + """.trimIndent(), model = model, parsingModel = parsingModel, ), ActorTypes.CodeReviewer to SimpleActor( prompt = """ - |Analyze the code summarized in the user's header-labeled code blocks. - |Review, look for bugs, and provide fixes. - |Provide implementations for missing functions. - | - |Response should use one or more code patches in diff format within ```diff code blocks. - |Each diff should be preceded by a header that identifies the file being modified. - |The diff format should use + for line additions, - for line deletions. - |The diff should include 2 lines of context before and after every change. - | - |Example: - | - |Here are the patches: - | - |### src/utils/exampleUtils.js - |```diff - | // Utility functions for example feature - | const b = 2; - | function exampleFunction() { - |- return b + 1; - |+ return b + 2; - | } - |``` - | - |### tests/exampleUtils.test.js - |```diff - | // Unit tests for exampleUtils - | const assert = require('assert'); - | const { exampleFunction } = require('../src/utils/exampleUtils'); - | - | describe('exampleFunction', () => { - |- it('should return 3', () => { - |+ it('should return 4', () => { - | assert.equal(exampleFunction(), 3); - | }); - | }); - |``` - """.trimMargin(), + Analyze the code summarized in the user's header-labeled code blocks. + Review, look for bugs, and provide fixes. + Provide implementations for missing functions. + + Response should use one or more code patches in diff format within ```diff code blocks. + Each diff should be preceded by a header that identifies the file being modified. + The diff format should use + for line additions, - for line deletions. + The diff should include 2 lines of context before and after every change. + + Example: + + Here are the patches: + + ### src/utils/exampleUtils.js + ```diff + // Utility functions for example feature + const b = 2; + function exampleFunction() { + - return b + 1; + + return b + 2; + } + ``` + + ### tests/exampleUtils.test.js + ```diff + // Unit tests for exampleUtils + const assert = require('assert'); + const { exampleFunction } = require('../src/utils/exampleUtils'); + + describe('exampleFunction', () => { + - it('should return 3', () => { + + it('should return 4', () => { + assert.equal(exampleFunction(), 3); + }); + }); + ``` + """.trimIndent(), model = model, ), ActorTypes.HtmlCodingActor to SimpleActor( prompt = """ - |You will translate the user request into a skeleton HTML file for a rich javascript application. - |The html file can reference needed CSS and JS files, which are will be located in the same directory as the html file. - |Do not output the content of the resource files, only the html file. - """.trimMargin(), model = model + You will translate the user request into a skeleton HTML file for a rich javascript application. + The html file can reference needed CSS and JS files, which are will be located in the same directory as the html file. + Do not output the content of the resource files, only the html file. + """.trimIndent(), model = model ), ActorTypes.JavascriptCodingActor to SimpleActor( prompt = """ - |You will translate the user request into a javascript file for use in a rich javascript application. - """.trimMargin(), model = model + You will translate the user request into a javascript file for use in a rich javascript application. + """.trimIndent(), model = model ), ActorTypes.CssCodingActor to SimpleActor( prompt = """ - |You will translate the user request into a CSS file for use in a rich javascript application. - """.trimMargin(), model = model + You will translate the user request into a CSS file for use in a rich javascript application. + """.trimIndent(), model = model ), ActorTypes.EtcCodingActor to SimpleActor( prompt = """ - |You will translate the user request into a file for use in a web application. - """.trimMargin(), + You will translate the user request into a file for use in a web application. + """.trimIndent(), model = model, ), ActorTypes.ImageActor to ImageActor( prompt = """ - |You will translate the user request into an image file for use in a web application. - """.trimMargin(), + You will translate the user request into an image file for use in a web application. + """.trimIndent(), textModel = model, imageModel = ImageModels.DallE3, ).apply { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/CodingActorDesigner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/CodingActorDesigner.kt index 327ef80e..118a2ffb 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/CodingActorDesigner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/CodingActorDesigner.kt @@ -12,62 +12,61 @@ class CodingActorDesigner( model: ChatModel, temperature: Double ) : CodingActor( - interpreterClass = interpreterClass, - symbols = symbols, + interpreterClass, symbols, details = """ - | - |Your task is to design a system that uses gpt "actors" to form a "community" of actors interacting to solve problems. - |Your task is to implement a "script" or "coding" actor that takes part in a larger system. - |"Script" actors use a multi-stage process that combines an environment definition of predefined symbols/functions and a pluggable script compilation system using Scala, Kotlin, or Groovy. The actor will return a valid script with a convenient "execute" method. This can provide both simple function calling responses and complex code generation. - | - |For context, here is the constructor signature for CodingActor class: - |```kotlin - |package com.simiacryptus.skyenet.core.actors - | - |import com.simiacryptus.jopenai.models.OpenAIModels - |import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber - |import com.simiacryptus.jopenai.describe.TypeDescriber - |import com.simiacryptus.skyenet.interpreter.Interpreter - |import kotlin.reflect.KClass - | - |class CodingActor( - | val interpreterClass: KClass, - | val symbols: Map = mapOf(), - | val describer: TypeDescriber = AbbrevWhitelistYamlDescriber( - | "com.simiacryptus", - | "com.github.simiacryptus" - | ), - | name: String? = interpreterClass.simpleName, - | val details: String? = null, - | model: OpenAITextModel = OpenAIModels.GPT4o, - | val fallbackModel: ChatModels = OpenAIModels.GPT4o, - | temperature: Double = 0.1, - |) - |``` - | - |In this code example an example actor is defined with a prompt, name, and a standard configuration: - |```kotlin - |import com.simiacryptus.skyenet.core.actors.CodingActor - |import com.simiacryptus.skyenet.kotlin.KotlinInterpreter - | - |fun exampleCodingActor() = CodingActor( - | interpreterClass = KotlinInterpreter::class, - | details = ""${'"'} - | |You are a software implementation assistant. - | | - | |Defined functions: - | |* ... - | | - | |Expected code structure: - | |* ... - | ""${'"'}.trimMargin().trim(), - |) - |``` - | - |Respond to the request with an instantiation function of the requested actor, similar to the provided example. - |DO NOT subclass the CodingActor class. Use the constructor directly within the function. - | - """.trimMargin().trim(), + + Your task is to design a system that uses gpt "actors" to form a "community" of actors interacting to solve problems. + Your task is to implement a "script" or "coding" actor that takes part in a larger system. + "Script" actors use a multi-stage process that combines an environment definition of predefined symbols/functions and a pluggable script compilation system using Scala, Kotlin, or Groovy. The actor will return a valid script with a convenient "execute" method. This can provide both simple function calling responses and complex code generation. + + For context, here is the constructor signature for CodingActor class: + ```kotlin + package com.simiacryptus.skyenet.core.actors + + import com.simiacryptus.jopenai.models.OpenAIModels + import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber + import com.simiacryptus.jopenai.describe.TypeDescriber + import com.simiacryptus.skyenet.interpreter.Interpreter + import kotlin.reflect.KClass + + class CodingActor( + val interpreterClass: KClass, + val symbols: Map = mapOf(), + val describer: TypeDescriber = AbbrevWhitelistYamlDescriber( + "com.simiacryptus", + "com.github.simiacryptus" + ), + name: String? = interpreterClass.simpleName, + val details: String? = null, + model: OpenAITextModel = OpenAIModels.GPT4o, + val fallbackModel: ChatModels = OpenAIModels.GPT4o, + temperature: Double = 0.1, + ) + ``` + + In this code example an example actor is defined with a prompt, name, and a standard configuration: + ```kotlin + import com.simiacryptus.skyenet.core.actors.CodingActor + import com.simiacryptus.skyenet.kotlin.KotlinInterpreter + + fun exampleCodingActor() = CodingActor( + interpreterClass = KotlinInterpreter::class, + details = ""${'"'} + You are a software implementation assistant. + + Defined functions: + * ... + + Expected code structure: + * ... + ""${'"'}.trimIndent(), + ) + ``` + + Respond to the request with an instantiation function of the requested actor, similar to the provided example. + DO NOT subclass the CodingActor class. Use the constructor directly within the function. + + """.trimIndent().trim(), model = model, temperature = temperature, ) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/FlowStepDesigner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/FlowStepDesigner.kt index 319b7944..41e0b3f4 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/FlowStepDesigner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/FlowStepDesigner.kt @@ -15,73 +15,73 @@ class FlowStepDesigner( interpreterClass = interpreterClass, symbols = symbols, details = """ - |You are a software implementor. - | - |Your task is to implement logic for an "agent" system that uses gpt "actors" to construct a model of a creative process. - |This "creative process" can be thought of as a cognitive process, an individual's work process, or an organizational process. - |The idea is that the overall structure is procedural and can be modeled in code, but individual steps are creative and can be modeled with gpt. - | - |Actors process inputs in the form of ChatGPT messages (often a single string) but vary in their output. - |Usage examples of each actor type follows: - | - |Simple actors contain a system directive, and simply return the chat model's response to the user query. - |Simple actors answer queries consisting of a list of strings representing a conversation thread, and respond with a string. - |```kotlin - |val actor : com.simiacryptus.skyenet.core.actors.SimpleActor - |val answer : String = actor.answer(listOf("This is an example question"), api = api) - |log.info("Answer: " + answer) - |``` - | - |Parsed actors use a 2-stage system; first, queries are responded in the same manner as simple actors using a system prompt. - |This natural-language response is then parsed into a typed object, which can be used in the application logic. - |Parsed actors answer queries consisting of a list of strings representing a conversation thread, and responds with an object containing text and a parsed object. - |```kotlin - |val actor : com.simiacryptus.skyenet.core.actors.ParsedActor - |val answer : com.simiacryptus.skyenet.core.actors.ParsedResponse = actor.answer(listOf("This is some example data"), api = api) - |log.info("Natural Language Answer: " + answer.text) - |log.info("Parsed Answer: " + com.simiacryptus.jopenai.util.JsonUtil.toJson(answer.obj)) - |``` - | - |Coding actors combine ChatGPT-powered code generation with compilation and validation to produce quality code without having to run it. - |Coding actors answer queries expressed using CodeRequest, and responds with an object that defines a code block and an execution method. - |```kotlin - |val actor : com.simiacryptus.skyenet.core.actors.CodingActor - |val answer : com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult = actor.answer(listOf("Do an example task"), api = api) - |log.info("Implemented Code: " + answer.code) - |val executionResult : com.simiacryptus.skyenet.core.actors.CodingActor.ExecutionResult = answer.result - |log.info("Execution Log: " + executionResult.resultOutput) - |log.info("Execution Result: " + executionResult.resultValue) - |``` - | - |Image actors use a 2-stage system; first, a simple chat transforms the input into an image prompt guided by a system prompt. - |Image actors answer queries consisting of a list of strings representing a conversation thread, and respond with an image. - |```kotlin - |val actor : com.simiacryptus.skyenet.core.actors.ImageActor - |val answer : com.simiacryptus.skyenet.core.actors.ImageResponse = actor.answer(listOf("Draw an example image"), api = api) - |log.info("Image description: " + answer.text) - |val image : BufferedImage = answer.image - |``` - | - |While implementing logic, the progress should be displayed to the user using the `ui` object. - |The UI display generally follows a pattern similar to: - |```kotlin - |val task = ui.newTask() - |try { - | task.header("Main Function") - | task.add("Normal message") - | task.verbose("Verbose output - not shown by default") - | task.add(ui.textInput { log.info("Message Received: " + it) }) - | task.add(ui.hrefLink("Click Me!") { log.info("Link clicked") }) - | task.complete() - |} catch (e: Throwable) { - | task.error(e) - | throw e - |} - |``` - | - |**IMPORTANT**: Do not redefine any symbol defined in the preceding code messages. - | - |""".trimMargin().trim(), + You are a software implementor. + + Your task is to implement logic for an "agent" system that uses gpt "actors" to construct a model of a creative process. + This "creative process" can be thought of as a cognitive process, an individual's work process, or an organizational process. + The idea is that the overall structure is procedural and can be modeled in code, but individual steps are creative and can be modeled with gpt. + + Actors process inputs in the form of ChatGPT messages (often a single string) but vary in their output. + Usage examples of each actor type follows: + + Simple actors contain a system directive, and simply return the chat model's response to the user query. + Simple actors answer queries consisting of a list of strings representing a conversation thread, and respond with a string. + ```kotlin + val actor : com.simiacryptus.skyenet.core.actors.SimpleActor + val answer : String = actor.answer(listOf("This is an example question"), api = api) + log.info("Answer: " + answer) + ``` + + Parsed actors use a 2-stage system; first, queries are responded in the same manner as simple actors using a system prompt. + This natural-language response is then parsed into a typed object, which can be used in the application logic. + Parsed actors answer queries consisting of a list of strings representing a conversation thread, and responds with an object containing text and a parsed object. + ```kotlin + val actor : com.simiacryptus.skyenet.core.actors.ParsedActor + val answer : com.simiacryptus.skyenet.core.actors.ParsedResponse = actor.answer(listOf("This is some example data"), api = api) + log.info("Natural Language Answer: " + answer.text) + log.info("Parsed Answer: " + com.simiacryptus.jopenai.util.JsonUtil.toJson(answer.obj)) + ``` + + Coding actors combine ChatGPT-powered code generation with compilation and validation to produce quality code without having to run it. + Coding actors answer queries expressed using CodeRequest, and responds with an object that defines a code block and an execution method. + ```kotlin + val actor : com.simiacryptus.skyenet.core.actors.CodingActor + val answer : com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult = actor.answer(listOf("Do an example task"), api = api) + log.info("Implemented Code: " + answer.code) + val executionResult : com.simiacryptus.skyenet.core.actors.CodingActor.ExecutionResult = answer.result + log.info("Execution Log: " + executionResult.resultOutput) + log.info("Execution Result: " + executionResult.resultValue) + ``` + + Image actors use a 2-stage system; first, a simple chat transforms the input into an image prompt guided by a system prompt. + Image actors answer queries consisting of a list of strings representing a conversation thread, and respond with an image. + ```kotlin + val actor : com.simiacryptus.skyenet.core.actors.ImageActor + val answer : com.simiacryptus.skyenet.core.actors.ImageResponse = actor.answer(listOf("Draw an example image"), api = api) + log.info("Image description: " + answer.text) + val image : BufferedImage = answer.image + ``` + + While implementing logic, the progress should be displayed to the user using the `ui` object. + The UI display generally follows a pattern similar to: + ```kotlin + val task = ui.newTask() + try { + task.header("Main Function") + task.add("Normal message") + task.verbose("Verbose output - not shown by default") + task.add(ui.textInput { log.info("Message Received: " + it) }) + task.add(ui.hrefLink("Click Me!") { log.info("Link clicked") }) + task.complete() + } catch (e: Throwable) { + task.error(e) + throw e + } + ``` + + **IMPORTANT**: Do not redefine any symbol defined in the preceding code messages. + + """.trimIndent(), model = model, temperature = temperature, runtimeSymbols = mapOf( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ImageActorDesigner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ImageActorDesigner.kt index 7815493d..792e7644 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ImageActorDesigner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ImageActorDesigner.kt @@ -15,43 +15,42 @@ class ImageActorDesigner( interpreterClass = interpreterClass, symbols = symbols, details = """ - | - |You are a software implementation assistant. - |Your task is to implement a "image" actor that takes part in a larger system. - |"Image" actors contain a system directive and can process a list of user messages into a response. - | - |For context, here is the constructor signature for ImageActor class: - |```kotlin - |import com.simiacryptus.jopenai.models.ChatModels - |import com.simiacryptus.jopenai.models.ImageModels - |import com.simiacryptus.skyenet.core.actors.ImageActor - | - |class ImageActor( - | prompt: String = "Transform the user request into an image generation prompt that the user will like", - | name: String? = null, - | textModel: ChatModels = OpenAIModels.GPT4oMini, - | val imageModel: ImageModels = ImageModels.DallE3, - | temperature: Double = 0.3, - | val width: Int = 1024, - | val height: Int = 1024, - |) - |``` - | - |In this code example an example actor is defined with a prompt and a name: - |```kotlin - |import com.simiacryptus.skyenet.core.actors.ImageActor - | - |fun exampleSimpleActor() = ImageActor( - | prompt = ""${'"'} - | |You are a writing assistant. - | ""${'"'}.trimMargin().trim(), - |) - |``` - | - |Respond to the request with an instantiation function of the requested actor, similar to the provided example. - |DO NOT subclass the ImageActor class. Use the constructor directly within the function. - | - """.trimMargin().trim(), + You are a software implementation assistant. + Your task is to implement a "image" actor that takes part in a larger system. + "Image" actors contain a system directive and can process a list of user messages into a response. + + For context, here is the constructor signature for ImageActor class: + ```kotlin + import com.simiacryptus.jopenai.models.ChatModels + import com.simiacryptus.jopenai.models.ImageModels + import com.simiacryptus.skyenet.core.actors.ImageActor + + class ImageActor( + prompt: String = "Transform the user request into an image generation prompt that the user will like", + name: String? = null, + textModel: ChatModels = OpenAIModels.GPT4oMini, + val imageModel: ImageModels = ImageModels.DallE3, + temperature: Double = 0.3, + val width: Int = 1024, + val height: Int = 1024, + ) + ``` + + In this code example an example actor is defined with a prompt and a name: + ```kotlin + import com.simiacryptus.skyenet.core.actors.ImageActor + + fun exampleSimpleActor() = ImageActor( + prompt = ""${'"'} + You are a writing assistant. + ""${'"'}.trimIndent(), + ) + ``` + + Respond to the request with an instantiation function of the requested actor, similar to the provided example. + DO NOT subclass the ImageActor class. Use the constructor directly within the function. + + """.trimIndent(), model = model, temperature = temperature, ) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/MetaAgentApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/MetaAgentApp.kt index 15ed3d48..ee3f185b 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/MetaAgentApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/MetaAgentApp.kt @@ -164,26 +164,26 @@ open class MetaAgentAgent( @Language("kotlin") val standardImports = """ - |import com.simiacryptus.jopenai.API - |import com.simiacryptus.jopenai.models.ChatModels - |import com.simiacryptus.skyenet.core.actors.BaseActor - |import com.simiacryptus.skyenet.core.actors.ActorSystem - |import com.simiacryptus.skyenet.core.actors.CodingActor - |import com.simiacryptus.skyenet.core.actors.ParsedActor - |import com.simiacryptus.skyenet.core.actors.ImageActor - |import com.simiacryptus.skyenet.core.platform.file.DataStorage - |import com.simiacryptus.skyenet.core.platform.Session - |import com.simiacryptus.skyenet.core.platform.StorageInterface - |import com.simiacryptus.skyenet.core.actors.PoolSystem - |import com.simiacryptus.skyenet.core.platform.User - |import com.simiacryptus.skyenet.webui.application.ApplicationServer - |import com.simiacryptus.skyenet.webui.session.* - |import com.simiacryptus.skyenet.webui.application.ApplicationInterface - |import java.awt.image.BufferedImage - |import org.slf4j.LoggerFactory - |import java.io.File - |import javax.imageio.ImageIO - """.trimMargin() + import com.simiacryptus.jopenai.API + import com.simiacryptus.jopenai.models.ChatModels + import com.simiacryptus.skyenet.core.actors.BaseActor + import com.simiacryptus.skyenet.core.actors.ActorSystem + import com.simiacryptus.skyenet.core.actors.CodingActor + import com.simiacryptus.skyenet.core.actors.ParsedActor + import com.simiacryptus.skyenet.core.actors.ImageActor + import com.simiacryptus.skyenet.core.platform.file.DataStorage + import com.simiacryptus.skyenet.core.platform.Session + import com.simiacryptus.skyenet.core.platform.StorageInterface + import com.simiacryptus.skyenet.core.actors.PoolSystem + import com.simiacryptus.skyenet.core.platform.User + import com.simiacryptus.skyenet.webui.application.ApplicationServer + import com.simiacryptus.skyenet.webui.session.* + import com.simiacryptus.skyenet.webui.application.ApplicationInterface + import java.awt.image.BufferedImage + import org.slf4j.LoggerFactory + import java.io.File + import javax.imageio.ImageIO + """.trimIndent() fun buildAgent(userMessage: String) { val design = initialDesign(userMessage) @@ -211,93 +211,93 @@ open class MetaAgentAgent( val actorInits = design.obj.actors?.joinToString("\n") { actImpls[it.name] ?: "" } ?: "" @Language("kotlin") val appCode = """ - |$standardImports - | - |$imports - | - |open class ${classBaseName}App( - | applicationName: String = "${design.obj.name}", - | path: String = "/${design.obj.path ?: ""}", - |) : ApplicationServer( - | applicationName = applicationName, - | path = path, - |) { - | - | data class Settings( - | val model: ChatModels = OpenAIModels.GPT4oMini, - | val temperature: Double = 0.1, - | ) - | override val settingsClass: Class<*> get() = Settings::class.java - | @Suppress("UNCHECKED_CAST") override fun initSettings(session: Session): T? = Settings() as T - | - | override fun userMessage( - | session: Session, - | user: User?, - | userMessage: String, - | ui: ApplicationInterface, - | api: API - | ) { - | try { - | val settings = getSettings(session, user) - | ${classBaseName}Agent( - | user = user, - | session = session, - | dataStorage = dataStorage, - | api = api, - | ui = ui, - | model = settings?.model ?: OpenAIModels.GPT4oMini, - | temperature = settings?.temperature ?: 0.3, - | ).${design.obj.name?.camelCase()}(userMessage) - | } catch (e: Throwable) { - | log.warn("Error", e) - | } - | } - | - | companion object { - | private val log = LoggerFactory.getLogger(${classBaseName}App::class.java) - | } - | - |} - """.trimMargin() + $standardImports + + $imports + + open class ${classBaseName}App( + applicationName: String = "${design.obj.name}", + path: String = "/${design.obj.path ?: ""}", + ) : ApplicationServer( + applicationName = applicationName, + path = path, + ) { + + data class Settings( + val model: ChatModels = OpenAIModels.GPT4oMini, + val temperature: Double = 0.1, + ) + override val settingsClass: Class<*> get() = Settings::class.java + @Suppress("UNCHECKED_CAST") override fun initSettings(session: Session): T? = Settings() as T + + override fun userMessage( + session: Session, + user: User?, + userMessage: String, + ui: ApplicationInterface, + api: API + ) { + try { + val settings = getSettings(session, user) + ${classBaseName}Agent( + user = user, + session = session, + dataStorage = dataStorage, + api = api, + ui = ui, + model = settings?.model ?: OpenAIModels.GPT4oMini, + temperature = settings?.temperature ?: 0.3, + ).${design.obj.name?.camelCase()}(userMessage) + } catch (e: Throwable) { + log.warn("Error", e) + } + } + + companion object { + private val log = LoggerFactory.getLogger(${classBaseName}App::class.java) + } + + } + """.trimIndent() @Language("kotlin") val agentCode = """ - |$standardImports - | - |open class ${classBaseName}Agent( - | user: User?, - | session: Session, - | dataStorage: StorageInterface, - | val ui: ApplicationInterface, - | val api: API, - | model: ChatModels = OpenAIModels.GPT4oMini, - | temperature: Double = 0.3, - |) : PoolSystem(dataStorage, user, session) { - | - | ${actorInits.indent(" ")} - | - | ${mainImpl.trimIndent().stripImports().indent(" ")} - | - | ${flowImpl.values.joinToString("\n\n") { flowStep -> flowStep.trimIndent() }.stripImports().indent(" ")} - | - | companion object { - | private val log = org.slf4j.LoggerFactory.getLogger(${classBaseName}Agent::class.java) - | - | } - |} - """.trimMargin() + $standardImports + + open class ${classBaseName}Agent( + user: User?, + session: Session, + dataStorage: StorageInterface, + val ui: ApplicationInterface, + val api: API, + model: ChatModels = OpenAIModels.GPT4oMini, + temperature: Double = 0.3, + ) : PoolSystem(dataStorage, user, session) { + + ${actorInits.indent(" ")} + + ${mainImpl.trimIndent().stripImports().indent(" ")} + + ${flowImpl.values.joinToString("\n\n") { flowStep -> flowStep.trimIndent() }.stripImports().indent(" ")} + + companion object { + private val log = org.slf4j.LoggerFactory.getLogger(${classBaseName}Agent::class.java) + + } + } + """.trimIndent() //language=MARKDOWN val code = """ - |```kotlin - |${ + ```kotlin + ${ """ - |$appCode - | - |$agentCode - """.trimMargin().sortCode() + $appCode + + $agentCode + """.trimIndent().sortCode() } - |``` - """.trimMargin() + ``` + """.trimIndent() //language=HTML task.complete(renderMarkdown(code, ui = ui)) @@ -336,12 +336,7 @@ open class MetaAgentAgent( outputFn = { design: ParsedResponse -> try { renderMarkdown( - """ - |$design - |```json - |${JsonUtil.toJson(design.obj)} - |``` - """.trimMargin(), ui = ui + "$design\n```json\n${JsonUtil.toJson(design.obj)}\n```", ui = ui ) } catch (e: Throwable) { renderMarkdown(e.message ?: e.toString(), ui = ui) @@ -365,12 +360,7 @@ open class MetaAgentAgent( outputFn = { design: ParsedResponse -> try { renderMarkdown( - """ - |$design - |```json - |${JsonUtil.toJson(design.obj)} - |``` - """.trimMargin(), ui = ui + "$design\n```json\n${JsonUtil.toJson(design.obj)}\n```", ui = ui ) } catch (e: Throwable) { renderMarkdown(e.message ?: e.toString(), ui = ui) @@ -421,11 +411,7 @@ open class MetaAgentAgent( val mainFunction = execWrap { flowStepDesigner.answer(codeRequest, api = api).code } task.verbose( renderMarkdown( - """ - |```kotlin - |$mainFunction - |``` - """.trimMargin(), ui = ui + "```kotlin\n$mainFunction\n```", ui = ui ), tag = "div" ) task.complete() @@ -488,6 +474,7 @@ open class MetaAgentAgent( var code = "" val onComplete = java.util.concurrent.Semaphore(0) Retryable(ui, task) { + val TT = "```" try { val response = execWrap { when (type.lowercase()) { @@ -501,27 +488,20 @@ open class MetaAgentAgent( code = response.code onComplete.release() renderMarkdown( - """ - |```kotlin - |$code - |``` - """.trimMargin(), ui = ui + "${TT}kotlin\n$code\n```", ui = ui ) } catch (e: CodingActor.FailedToImplementException) { task.error(ui, e) code = e.code ?: "" renderMarkdown( """ - |```kotlin - |$code - |``` - |${ - ui.hrefLink("Accept", classname = "href-link cmd-button") { - autoEvaluate = false - onComplete.release() - } - } - """.trimMargin(), ui = ui + ${TT}kotlin + """.trimIndent() + code + """ + $TT + """.trimIndent() + ui.hrefLink("Accept", classname = "href-link cmd-button") { + autoEvaluate = false + onComplete.release() + }, ui = ui ) } } @@ -581,27 +561,16 @@ open class MetaAgentAgent( } onComplete.release() renderMarkdown( - """ - |```kotlin - |$code - |``` - """.trimMargin(), ui = ui + "```kotlin\n$code\n```", ui = ui ) } catch (e: CodingActor.FailedToImplementException) { message.error(ui, e) code = e.code ?: "" renderMarkdown( - """ - |```kotlin - |$code - |``` - |${ - ui.hrefLink("Accept", classname = "href-link cmd-button") { - autoEvaluate = false - onComplete.release() - } - } - """.trimMargin(), ui = ui + "```kotlin" + code + "```" + ui.hrefLink("Accept", classname = "href-link cmd-button") { + autoEvaluate = false + onComplete.release() + }, ui = ui ) } } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ParsedActorDesigner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ParsedActorDesigner.kt index edaef383..5e6711d6 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ParsedActorDesigner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/ParsedActorDesigner.kt @@ -17,58 +17,57 @@ class ParsedActorDesigner( interpreterClass = interpreterClass, symbols = symbols, details = """ - | - |Your task is to design a system that uses gpt "actors" to form a "community" of actors interacting to solve problems. - |Your task is to implement a "parsed" actor that takes part in a larger system. - |"Parsed" actors use a 2-stage system; first, queries are responded in the same manner as simple actors. A second pass uses GPT3.5_Turbo to parse the text response into a predefined kotlin data class - | - |For context, here is the constructor signature for ParsedActor class: - |```kotlin - |import com.simiacryptus.jopenai.models.ChatModels - |import com.simiacryptus.jopenai.models.OpenAIModels - |import java.util.function.Function - | - |open class ParsedActor( - | val resultClass: Class, - | val exampleInstance: T? = resultClass.getConstructor().newInstance(), - | prompt: String, - | val name: String? = null, - | model: ChatModels = OpenAIModels.GPT4oMini, - | temperature: Double = 0.3, - |) - |``` - | - |In this code example an example actor is defined with a prompt, name, and parsing class: - |```kotlin - |import com.simiacryptus.jopenai.describe.Description - |import com.simiacryptus.jopenai.models.ChatModels - |import com.simiacryptus.jopenai.proxy.ValidatedObject - |import com.simiacryptus.skyenet.core.actors.ParsedActor - |import java.util.function.Function - | - |data class ExampleResult( - | @Description("The name of the example") - | val name: String? = null, - |) : ValidatedObject { - | override fun validate() = when { - | name.isNullOrBlank() -> "name is required" - | else -> null - | } - |} - | - |fun exampleParsedActor() = ParsedActor( - | resultClass = ExampleResult::class.java, - | model = OpenAIModels.GPT4o, - | prompt = ""${'"'} - | |You are a question answering assistant. - | |""${'"'}.trimMargin().trim(), - |) - |``` - | - |Respond to the request with an instantiation function of the requested actor, similar to the provided example. - |DO NOT subclass the ParsedActor class. Use the constructor directly within the function. - | - """.trimMargin().trim(), + Your task is to design a system that uses gpt "actors" to form a "community" of actors interacting to solve problems. + Your task is to implement a "parsed" actor that takes part in a larger system. + "Parsed" actors use a 2-stage system; first, queries are responded in the same manner as simple actors. A second pass uses GPT3.5_Turbo to parse the text response into a predefined kotlin data class + + For context, here is the constructor signature for ParsedActor class: + ```kotlin + import com.simiacryptus.jopenai.models.ChatModels + import com.simiacryptus.jopenai.models.OpenAIModels + import java.util.function.Function + + open class ParsedActor( + val resultClass: Class, + val exampleInstance: T? = resultClass.getConstructor().newInstance(), + prompt: String, + val name: String? = null, + model: ChatModels = OpenAIModels.GPT4oMini, + temperature: Double = 0.3, + ) + ``` + + In this code example an example actor is defined with a prompt, name, and parsing class: + ```kotlin + import com.simiacryptus.jopenai.describe.Description + import com.simiacryptus.jopenai.models.ChatModels + import com.simiacryptus.jopenai.proxy.ValidatedObject + import com.simiacryptus.skyenet.core.actors.ParsedActor + import java.util.function.Function + + data class ExampleResult( + @Description("The name of the example") + val name: String? = null, + ) : ValidatedObject { + override fun validate() = when { + name.isNullOrBlank() -> "name is required" + else -> null + } + } + + fun exampleParsedActor() = ParsedActor( + resultClass = ExampleResult::class.java, + model = OpenAIModels.GPT4o, + prompt = ""${'"'} + You are a question answering assistant. + ""${'"'}.trimIndent(), + ) + ``` + + Respond to the request with an instantiation function of the requested actor, similar to the provided example. + DO NOT subclass the ParsedActor class. Use the constructor directly within the function. + + """.trimIndent(), model = model, temperature = temperature, ) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/SimpleActorDesigner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/SimpleActorDesigner.kt index 0cd5ced9..be4f1e9a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/SimpleActorDesigner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/meta/SimpleActorDesigner.kt @@ -44,8 +44,8 @@ class SimpleActorDesigner( @Language("Markdown")fun exampleSimpleActor() = SimpleActor( prompt = "${'"'}" - |You are a writing assistant. - "${'"'}".trimMargin().trim(), + You are a writing assistant. + "${'"'}".trimIndent(), ) ``` diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/CodeParsingModel.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/CodeParsingModel.kt index d8cae83a..b82910ba 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/CodeParsingModel.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/CodeParsingModel.kt @@ -46,13 +46,13 @@ open class CodeParsingModel( ) open val promptSuffix = """ -Parse the code into a structured format that describes its components: -1. Separate the content into sections, paragraphs, statements, etc. -2. All source content should be included in the output, with paraphrasing, corrections, and context as needed -3. Each content leaf node text should be simple and self-contained -4. Assign relevant tags to each node to improve searchability and categorization -5. Track line numbers and character positions for each content node when possible - """.trimMargin() + Parse the code into a structured format that describes its components: + 1. Separate the content into sections, paragraphs, statements, etc. + 2. All source content should be included in the output, with paraphrasing, corrections, and context as needed + 3. Each content leaf node text should be simple and self-contained + 4. Assign relevant tags to each node to improve searchability and categorization + 5. Track line numbers and character positions for each content node when possible + """.trimIndent() open val exampleInstance = CodeData() diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParserApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParserApp.kt index ad2ed28a..9620748d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParserApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParserApp.kt @@ -99,7 +99,7 @@ open class DocumentParserApp( throw IllegalArgumentException("No input files provided") } - mainTask.header("PDF Extractor") + mainTask.header("Knowledge Extractor") val api = (api as ChatClient).getChildClient().apply { val createFile = mainTask.createFile(".logs/api-${UUID.randomUUID()}.log") createFile.second?.apply { @@ -148,21 +148,25 @@ open class DocumentParserApp( val pageTabs = TabbedDisplay(pageTask.apply { pageTabs[label] = placeholder }) if (settings.showImages) { for (pageIndex in batchStart until batchEnd) { - val image = reader.renderImage(pageIndex, settings.dpi) - ui.newTask(false).apply { - pageTabs["Image ${1 + (pageIndex - batchStart)}"] = placeholder - image(image) - } - if (settings.saveImageFiles) { - val imageFile = - outputDir.resolve("page_${pageIndex}.${settings.outputFormat.lowercase(Locale.getDefault())}") - when (settings.outputFormat.uppercase(Locale.getDefault())) { - "PNG" -> ImageIO.write(image, "PNG", imageFile) - "JPEG", "JPG" -> ImageIO.write(image, "JPEG", imageFile) - "GIF" -> ImageIO.write(image, "GIF", imageFile) - "BMP" -> ImageIO.write(image, "BMP", imageFile) - else -> throw IllegalArgumentException("Unsupported output format: ${settings.outputFormat}") + try { + val image = reader.renderImage(pageIndex, settings.dpi) + ui.newTask(false).apply { + pageTabs["Image ${1 + (pageIndex - batchStart)}"] = placeholder + image(image) + } + if (settings.saveImageFiles) { + val imageFile = + outputDir.resolve("page_${pageIndex}.${settings.outputFormat.lowercase(Locale.getDefault())}") + when (settings.outputFormat.uppercase(Locale.getDefault())) { + "PNG" -> ImageIO.write(image, "PNG", imageFile) + "JPEG", "JPG" -> ImageIO.write(image, "JPEG", imageFile) + "GIF" -> ImageIO.write(image, "GIF", imageFile) + "BMP" -> ImageIO.write(image, "BMP", imageFile) + else -> throw IllegalArgumentException("Unsupported output format: ${settings.outputFormat}") + } } + } catch (e: Throwable) { + log.info("Error rendering image for page $pageIndex", e) } } } @@ -175,23 +179,10 @@ open class DocumentParserApp( } val promptList = mutableListOf() promptList.add( - """ - |# Prior Text - | - |FOR INFORMATIVE CONTEXT ONLY. DO NOT COPY TO OUTPUT. - |```text - |$previousPageText - |``` - |""".trimMargin() + "# Prior Text\n\nFOR INFORMATIVE CONTEXT ONLY. DO NOT COPY TO OUTPUT.\n```text\n$previousPageText\n```" ) promptList.add( - """ - |# Current Page - | - |```text - |$text - |``` - """.trimMargin() + "# Current Page\n\n```text\n$text\n```" ) previousPageText = text if (fastMode) { @@ -220,26 +211,14 @@ open class DocumentParserApp( { runningDocument, it -> parsingModel.merge(runningDocument, it) } docTask.add( MarkdownUtil.renderMarkdown( - """ - |## Document JSON - | - |```json - |${JsonUtil.toJson(finalDocument)} - |``` - | - |Extracted files are saved in: ${outputDir.absolutePath} - """.trimMargin(), ui = ui + "## Document JSON\n\n```json\n${JsonUtil.toJson(finalDocument)}\n```\n\nExtracted files are saved in: ${outputDir.absolutePath}", ui = ui ) ) if (settings.saveFinalJson) { - val finalJsonFile = file.parentFile.resolve(file.name.reversed().split(delimiters = arrayOf("."), false, 2).joinToString("_").reversed() + ".parsed.json") + val finalJsonFile = + file.parentFile.resolve(file.name.reversed().split(delimiters = arrayOf("."), false, 2).joinToString("_").reversed() + ".parsed.json") finalJsonFile.writeText(JsonUtil.toJson(finalDocument)) - docTask.add( - MarkdownUtil.renderMarkdown( - "Final JSON saved to: ${finalJsonFile.absolutePath}", - ui = ui - ) - ) + docTask.add(MarkdownUtil.renderMarkdown("Final JSON saved to: ${finalJsonFile.absolutePath}", ui = ui)) } } } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParsingModel.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParsingModel.kt index 63b81908..9f0c374c 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParsingModel.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/DocumentParsingModel.kt @@ -49,12 +49,12 @@ open class DocumentParsingModel( ) open val promptSuffix = """ - |Parse the text into a hierarchical structure: - |1. Separate the content into sections, paragraphs, statements, etc. - |2. All source content should be included in the output, with paraphrasing, corrections, and context as needed - |3. Each content leaf node text should be simple and self-contained - |4. Assign relevant tags to each node to improve searchability and categorization. - """.trimMargin() + Parse the text into a hierarchical structure: + 1. Separate the content into sections, paragraphs, statements, etc. + 2. All source content should be included in the output, with paraphrasing, corrections, and context as needed + 3. Each content leaf node text should be simple and self-contained + 4. Assign relevant tags to each node to improve searchability and categorization. + """.trimIndent() open val exampleInstance = DocumentData() diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/LogPatternGenerator.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/LogPatternGenerator.kt index 603d6ccf..632a834e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/LogPatternGenerator.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/parse/LogPatternGenerator.kt @@ -23,7 +23,7 @@ class LogPatternGenerator( 4. Describe what type of log message the pattern identifies Return only the regex patterns with descriptions, no matches or analysis. - """.trimMargin() + """.trimIndent() fun generatePatterns(api: API, text: String): List { val parser = ParsedActor( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/AbstractTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/AbstractTask.kt index d7f0d242..3893c36b 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/AbstractTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/AbstractTask.kt @@ -28,11 +28,7 @@ abstract class AbstractTask( open fun getPriorCode(planProcessingState: PlanProcessingState) = taskConfig?.task_dependencies?.joinToString("\n\n\n") { dependency -> - """ - |# $dependency - | - |${planProcessingState.taskResult[dependency] ?: ""} - """.trimMargin() + "# $dependency\n\n${planProcessingState.taskResult[dependency] ?: ""}" } ?: "" diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanCoordinator.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanCoordinator.kt index f068462f..468ed790 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanCoordinator.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanCoordinator.kt @@ -15,7 +15,7 @@ import com.simiacryptus.skyenet.core.platform.Session import com.simiacryptus.skyenet.core.platform.model.StorageInterface import com.simiacryptus.skyenet.core.platform.model.User import com.simiacryptus.skyenet.set -import com.simiacryptus.skyenet.util.MarkdownUtil +import com.simiacryptus.skyenet.util.MarkdownUtil.renderMarkdown import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.util.JsonUtil @@ -68,15 +68,8 @@ class PlanCoordinator( taskBreakdownWithPrompt.plan } task.add( - MarkdownUtil.renderMarkdown( - """ - |## Executing TaskBreakdownWithPrompt - |Prompt: ${taskBreakdownWithPrompt.prompt} - |Plan Text: - |``` - |${taskBreakdownWithPrompt.planText} - |``` - """.trimMargin(), ui = ui + renderMarkdown( + "## Executing TaskBreakdownWithPrompt\nPrompt: ${taskBreakdownWithPrompt.prompt}\nPlan Text:\n```\n${taskBreakdownWithPrompt.planText}\n```", ui = ui ) ) executePlan(plan ?: emptyMap(), task, taskBreakdownWithPrompt.prompt, api, api2) @@ -105,7 +98,7 @@ class PlanCoordinator( executePlan( task = task, diagramBuffer = diagramTask.add( - MarkdownUtil.renderMarkdown( + renderMarkdown( "## Task Dependency Graph\n${TRIPLE_TILDE}mermaid\n${buildMermaidGraph(planProcessingState.subTasks)}\n$TRIPLE_TILDE", ui = ui ), additionalClasses = "flow-chart" @@ -157,13 +150,8 @@ class PlanCoordinator( val taskTabs = object : TabbedDisplay(sessionTask, additionalClasses = "task-tabs") { override fun renderTabButtons(): String { diagramBuffer?.set( - MarkdownUtil.renderMarkdown( - """ - |## Task Dependency Graph - |${TRIPLE_TILDE}mermaid - |${buildMermaidGraph(subTasks)} - |$TRIPLE_TILDE - """.trimMargin(), ui = ui + renderMarkdown( + "## Task Dependency Graph\n${TRIPLE_TILDE}mermaid\n${buildMermaidGraph(subTasks)}\n$TRIPLE_TILDE", ui = ui ) ) diagramTask.complete() @@ -227,19 +215,17 @@ class PlanCoordinator( ) task1.add( - MarkdownUtil.renderMarkdown( + renderMarkdown( """ - |## Task `${taskId}` - |${subTask.task_description ?: ""} - | - |${TRIPLE_TILDE}json - |${JsonUtil.toJson(data = subTask)/*.indent(" ")*/} - |$TRIPLE_TILDE - | - |### Dependencies: - |${dependencies.joinToString("\n") { "- $it" }} - | - """.trimMargin(), ui = ui + ## Task `""".trimIndent() + taskId + """` + """.trimIndent() + (subTask.task_description ?: "") + """ + + """.trimIndent() + TRIPLE_TILDE + """json + """.trimIndent() + JsonUtil.toJson(data = subTask) + """ + """.trimIndent() + TRIPLE_TILDE + """ + + ### Dependencies: + """.trimIndent() + dependencies.joinToString("\n") { "- $it" }, ui = ui ) ) val api = api.getChildClient().apply { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanSettings.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanSettings.kt index 665eb79a..b81b1a35 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanSettings.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanSettings.kt @@ -2,14 +2,14 @@ package com.simiacryptus.skyenet.apps.plan import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber import com.simiacryptus.jopenai.models.ChatModel -import com.simiacryptus.skyenet.apps.plan.tools.CommandAutoFixTask.CommandAutoFixTaskConfigData import com.simiacryptus.skyenet.apps.plan.PlanUtil.isWindows -import com.simiacryptus.skyenet.apps.plan.tools.PlanningTask.PlanningTaskConfigData -import com.simiacryptus.skyenet.apps.plan.tools.PlanningTask.TaskBreakdownResult import com.simiacryptus.skyenet.apps.plan.TaskType.Companion.getAvailableTaskTypes import com.simiacryptus.skyenet.apps.plan.TaskType.Companion.getImpl -import com.simiacryptus.skyenet.apps.plan.tools.file.FileModificationTask.FileModificationTaskConfigData import com.simiacryptus.skyenet.apps.plan.tools.CommandAutoFixTask +import com.simiacryptus.skyenet.apps.plan.tools.CommandAutoFixTask.CommandAutoFixTaskConfigData +import com.simiacryptus.skyenet.apps.plan.tools.PlanningTask.PlanningTaskConfigData +import com.simiacryptus.skyenet.apps.plan.tools.PlanningTask.TaskBreakdownResult +import com.simiacryptus.skyenet.apps.plan.tools.file.FileModificationTask.FileModificationTaskConfigData import com.simiacryptus.skyenet.core.actors.ParsedActor @@ -21,8 +21,7 @@ open class PlanSettings( val budget: Double = 2.0, val taskSettings: MutableMap = TaskType.values().associateWith { taskType -> TaskSettingsBase( - taskType.name, - when (taskType) { + taskType.name, when (taskType) { TaskType.FileModification, TaskType.Inquiry -> true else -> false } @@ -38,8 +37,7 @@ open class PlanSettings( var googleSearchEngineId: String? = null, ) { - fun getTaskSettings(taskType: TaskType<*, *>): TaskSettingsBase = - taskSettings[taskType.name] ?: TaskSettingsBase(taskType.name) + fun getTaskSettings(taskType: TaskType<*, *>): TaskSettingsBase = taskSettings[taskType.name] ?: TaskSettingsBase(taskType.name) fun setTaskSettings(taskType: TaskType<*, *>, settings: TaskSettingsBase) { taskSettings[taskType.name] = settings @@ -77,24 +75,21 @@ open class PlanSettings( fun planningActor(): ParsedActor { val planTaskSettings = this.getTaskSettings(TaskType.TaskPlanning) val prompt = """ - |Given a user request, identify and list smaller, actionable tasks that can be directly implemented in code. - | - |For each task: - |* Detail files input and output file - |* Describe task execution dependencies and order - |* Provide a brief description of the task - |* Specify important interface and integration details (each task will run independently off of a copy of this plan) - | - |Tasks can be of the following types: - |${ - getAvailableTaskTypes(this).joinToString("\n") { taskType -> - "* ${getImpl(this, taskType).promptSegment()}" - } - } - | - |Creating directories and initializing source control are out of scope. - |${if (planTaskSettings.enabled) "Do not start your plan with a plan to plan!\n" else ""} - """.trimMargin() + Given a user request, identify and list smaller, actionable tasks that can be directly implemented in code. + + For each task: + * Detail files input and output file + * Describe task execution dependencies and order + * Provide a brief description of the task + * Specify important interface and integration details (each task will run independently off of a copy of this plan) + + Tasks can be of the following types: + """.trimIndent() + getAvailableTaskTypes(this).joinToString("\n") { taskType -> + "* ${getImpl(this, taskType).promptSegment()}" + } + """ + + Creating directories and initializing source control are out of scope. + """.trimIndent() + (if (planTaskSettings.enabled) "Do not start your plan with a plan to plan!\n" else "") val describer = describer() val parserPrompt = """ Task Subtype Schema: @@ -139,22 +134,17 @@ ${taskType.name}: var exampleInstance = TaskBreakdownResult( tasksByID = mapOf( "1" to CommandAutoFixTaskConfigData( - task_description = "Task 1", - task_dependencies = listOf(), - commands = listOf( + task_description = "Task 1", task_dependencies = listOf(), commands = listOf( CommandAutoFixTask.CommandWithWorkingDir( - command = listOf("echo", "Hello, World!"), - workingDir = "." + command = listOf("echo", "Hello, World!"), workingDir = "." ) ) - ), - "2" to FileModificationTaskConfigData( + ), "2" to FileModificationTaskConfigData( task_description = "Task 2", task_dependencies = listOf("1"), input_files = listOf("input2.txt"), output_files = listOf("output2.txt"), - ), - "3" to PlanningTaskConfigData( + ), "3" to PlanningTaskConfigData( task_description = "Task 3", task_dependencies = listOf("2"), ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanUtil.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanUtil.kt index 330fb5f6..12860798 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanUtil.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/PlanUtil.kt @@ -15,12 +15,7 @@ object PlanUtil { ui: ApplicationInterface, taskMap: Map ) = MarkdownUtil.renderMarkdown( - """ - |## Sub-Plan Task Dependency Graph - |${TRIPLE_TILDE}mermaid - |${buildMermaidGraph(taskMap)} - |$TRIPLE_TILDE - """.trimMargin(), + "## Sub-Plan Task Dependency Graph\n${TRIPLE_TILDE}mermaid\n${buildMermaidGraph(taskMap)}\n$TRIPLE_TILDE", ui = ui ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/Planner.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/Planner.kt index ad73e3f6..6b54e9f2 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/Planner.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/Planner.kt @@ -110,17 +110,10 @@ open class Planner { root: Path ) = { str: String -> listOf( - if (!codeFiles.all { it.key.toFile().isFile } || codeFiles.size > 2) """ - |Files: - |${codeFiles.keys.joinToString("\n") { "* $it" }} - """.trimMargin() else { + if (!codeFiles.all { it.key.toFile().isFile } || codeFiles.size > 2) "Files:\n${codeFiles.keys.joinToString("\n") { "* $it" }} " else { files.joinToString("\n\n") { val path = root.relativize(it.toPath()) - """ - |## $path - | - |${(codeFiles[path] ?: "").let { "$TRIPLE_TILDE\n${it/*.indent(" ")*/}\n$TRIPLE_TILDE" }} - """.trimMargin() + "## $path\n\n${(codeFiles[path] ?: "").let { "$TRIPLE_TILDE\n${it}\n$TRIPLE_TILDE" }}" } }, str diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandAutoFixTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandAutoFixTask.kt index 6814d7ca..bfe2d6c7 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandAutoFixTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandAutoFixTask.kt @@ -43,7 +43,7 @@ class CommandAutoFixTask( data class CommandWithWorkingDir( @Description("The command to be executed") - val command: List, + val command: List = emptyList(), @Description("The relative path of the working directory") val workingDir: String? = null ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandSessionTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandSessionTask.kt index 98e69566..5bd353ff 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandSessionTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/CommandSessionTask.kt @@ -94,15 +94,14 @@ class CommandSessionTask( " ** Session $id" } return """ - CommandSession - Create and manage a stateful interactive command session - ** Specify the command to start an interactive session - ** Provide inputs to send to the session - ** Session persists between commands for stateful interactions - ** Optionally specify sessionId to reuse an existing session - ** Set closeSession=true to close the session after execution - Active Sessions: - $activeSessionsInfo - """.trimMargin() + CommandSession - Create and manage a stateful interactive command session + ** Specify the command to start an interactive session + ** Provide inputs to send to the session + ** Session persists between commands for stateful interactions + ** Optionally specify sessionId to reuse an existing session + ** Set closeSession=true to close the session after execution + Active Sessions: + """.trimIndent() + "\n" + activeSessionsInfo } override fun run( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GitHubSearchTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GitHubSearchTask.kt index 29d853a2..8ef73339 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GitHubSearchTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GitHubSearchTask.kt @@ -40,13 +40,13 @@ class GitHubSearchTask( ) override fun promptSegment() = """ -GitHubSearch - Search GitHub for code, commits, issues, repositories, topics, or users -** Specify the search query -** Specify the type of search (code, commits, issues, repositories, topics, users) -** Specify the number of results to return (max 100) -** Optionally specify sort order (e.g. stars, forks, updated) -** Optionally specify sort direction (asc or desc) -""".trimMargin() + GitHubSearch - Search GitHub for code, commits, issues, repositories, topics, or users + ** Specify the search query + ** Specify the type of search (code, commits, issues, repositories, topics, users) + ** Specify the number of results to return (max 100) + ** Optionally specify sort order (e.g. stars, forks, updated) + ** Optionally specify sort direction (asc or desc) + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GoogleSearchTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GoogleSearchTask.kt index 23dfe8c5..dba0da0e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GoogleSearchTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/GoogleSearchTask.kt @@ -36,10 +36,10 @@ class GoogleSearchTask( ) override fun promptSegment() = """ -GoogleSearch - Search Google for web results -** Specify the search query -** Specify the number of results to return (max 10) -""".trimMargin() + GoogleSearch - Search Google for web results + ** Specify the search query + ** Specify the number of results to return (max 10) + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/PlanningTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/PlanningTask.kt index a1453978..494ca9e8 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/PlanningTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/PlanningTask.kt @@ -35,19 +35,17 @@ class PlanningTask( val tasksByID: Map? = null, ) - override fun promptSegment(): String { - return """ - |Task Planning: - | * Perform high-level planning and organization of tasks. - | * Decompose the overall goal into smaller, actionable tasks based on current information, ensuring proper information flow between tasks. - | * Specify prior tasks and the overall goal of the task, emphasizing dependencies to ensure each task is connected with its upstream and downstream tasks. - | * Dynamically break down tasks as new information becomes available. - | * Carefully consider task dependencies to ensure efficient information transfer and coordination between tasks. - | * Design the task structure to maximize parallel execution where possible, while respecting necessary dependencies. - | * **Note**: A planning task should refine the plan based on new information, optimizing task relationships and dependencies, and should not initiate execution. - | * Ensure that each task utilizes the outputs or side effects of its upstream tasks, and provides outputs or side effects for its downstream tasks. - """.trimMargin() - } + override fun promptSegment() = """ + Task Planning: + * Perform high-level planning and organization of tasks. + * Decompose the overall goal into smaller, actionable tasks based on current information, ensuring proper information flow between tasks. + * Specify prior tasks and the overall goal of the task, emphasizing dependencies to ensure each task is connected with its upstream and downstream tasks. + * Dynamically break down tasks as new information becomes available. + * Carefully consider task dependencies to ensure efficient information transfer and coordination between tasks. + * Design the task structure to maximize parallel execution where possible, while respecting necessary dependencies. + * **Note**: A planning task should refine the plan based on new information, optimizing task relationships and dependencies, and should not initiate execution. + * Ensure that each task utilizes the outputs or side effects of its upstream tasks, and provides outputs or side effects for its downstream tasks. + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/RunShellCommandTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/RunShellCommandTask.kt index 6a96e2d0..10c733d9 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/RunShellCommandTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/RunShellCommandTask.kt @@ -39,9 +39,9 @@ class RunShellCommandTask( name = "RunShellCommand", interpreterClass = ProcessInterpreter::class, details = """ -Execute the following shell command(s) and provide the output. Ensure to handle any errors or exceptions gracefully. -Note: This task is for running simple and safe commands. Avoid executing commands that can cause harm to the system or compromise security. - """.trimMargin(), + Execute the following shell command(s) and provide the output. Ensure to handle any errors or exceptions gracefully. + Note: This task is for running simple and safe commands. Avoid executing commands that can cause harm to the system or compromise security. + """.trimIndent(), symbols = mapOf( "env" to (planSettings.env ?: emptyMap()), "workingDir" to (planTask?.workingDir?.let { File(it).absolutePath } ?: File( @@ -56,14 +56,12 @@ Note: This task is for running simple and safe commands. Avoid executing command ) } - override fun promptSegment(): String { - return """ - |RunShellCommand - Execute shell commands and provide the output - | ** Specify the command to be executed, or describe the task to be performed - | ** List input files/tasks to be examined when writing the command - | ** Optionally specify a working directory for the command execution - """.trimMargin() - } + override fun promptSegment() = """ + RunShellCommand - Execute shell commands and provide the output + ** Specify the command to be executed, or describe the task to be performed + ** List input files/tasks to be examined when writing the command + ** Optionally specify a working directory for the command execution + """.trimIndent() override fun run( agent: PlanCoordinator, @@ -96,13 +94,14 @@ Note: This task is for running simple and safe commands. Avoid executing command val formText = StringBuilder() var formHandle: StringBuilder? = null formHandle = task.add( - """ -
- ${if (!super.canPlay) "" else super.playButton(task, request, response, formText) { formHandle!! }} - ${acceptButton(response)} -
- ${super.reviseMsg(task, request, response, formText) { formHandle!! }} - """.trimMargin(), additionalClasses = "reply-message" + "
\n${ + if (!super.canPlay) "" else super.playButton( + task, + request, + response, + formText + ) { formHandle!! } + }\n${acceptButton(response)}\n
\n${super.reviseMsg(task, request, response, formText) { formHandle!! }}", additionalClasses = "reply-message" ) formText.append(formHandle.toString()) formHandle.toString() @@ -114,18 +113,7 @@ Note: This task is for running simple and safe commands. Avoid executing command ): String { return ui.hrefLink("Accept", "href-link play-button") { response.let { - """ - |## Shell Command Output - | - |$TRIPLE_TILDE - |${response.code} - |$TRIPLE_TILDE - | - |$TRIPLE_TILDE - |${response.renderedResponse} - |$TRIPLE_TILDE - | - """.trimMargin() + "## Shell Command Output\n\n$TRIPLE_TILDE\n${response.code}\n$TRIPLE_TILDE\n\n$TRIPLE_TILDE\n${response.renderedResponse}\n$TRIPLE_TILDE\n" }.apply { resultFn(this) } semaphore.release() } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SearchTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SearchTask.kt index 33f4008e..b5a02e3e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SearchTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SearchTask.kt @@ -37,12 +37,12 @@ class SearchTask( ) override fun promptSegment() = """ -Search - Search for patterns in files and provide results with context - ** Specify the search pattern (substring or regex) - ** Specify whether the pattern is a regex or a substring - ** Specify the number of context lines to include - ** List input files or file patterns to be searched - """.trimMargin() + Search - Search for patterns in files and provide results with context + ** Specify the search pattern (substring or regex) + ** Specify whether the pattern is a regex or a substring + ** Specify the number of context lines to include + ** List input files or file patterns to be searched + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SeleniumSessionTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SeleniumSessionTask.kt index 7d4ff6c2..7368a367 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SeleniumSessionTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/SeleniumSessionTask.kt @@ -117,48 +117,44 @@ class SeleniumSessionTask( state = state ) - override fun promptSegment(): String { - val activeSessionsInfo = activeSessions.entries.joinToString("\n") { (id, session: Selenium) -> - buildString { - append(" ** Session $id:\n") - append(" URL: ${session.getCurrentUrl()}\n") - try { - append(" Title: ${session.executeScript("return document.title;")}\n") - val logs = session.getLogs() - if (logs.isNotEmpty()) { - append(" Recent Logs:\n") - logs.takeLast(3).forEach { log -> - append(" - $log\n") + override fun promptSegment() = """ + SeleniumSession - Create and manage a stateful Selenium browser session + * Specify the URL to navigate to + * Provide JavaScript commands to execute in sequence through Selenium's executeScript method + * Can be used for web scraping, testing, or automation + * Session persists between commands for stateful interactions + * Optionally specify sessionId to reuse an existing session + * Set closeSession=true to close the session after execution + Example JavaScript Commands: + * "return document.title;" - Get page title + * "return document.querySelector('.my-class').textContent;" - Get element text + * "return Array.from(document.querySelectorAll('a')).map(a => a.href);" - Get all links + * "document.querySelector('#my-button').click();" - Click an element + * "window.scrollTo(0, document.body.scrollHeight);" - Scroll to bottom + * "return document.documentElement.outerHTML;" - Get entire page HTML + * "return new Promise(r => setTimeout(() => r(document.title), 1000));" - Async operation + Note: Commands are executed in the browser context and must be valid JavaScript. + Use proper error handling and waits for dynamic content. + + Active Sessions: + """.trimIndent() + activeSessions.entries.joinToString("\n") { (id, session: Selenium) -> + buildString { + append(" ** Session $id:\n") + append(" URL: ${session.getCurrentUrl()}\n") + try { + append(" Title: ${session.executeScript("return document.title;")}\n") + val logs = session.getLogs() + if (logs.isNotEmpty()) { + append(" Recent Logs:\n") + logs.takeLast(3).forEach { log -> + append(" - $log\n") + } } + } catch (e: Exception) { + append(" Error getting session details: ${e.message}\n") } - } catch (e: Exception) { - append(" Error getting session details: ${e.message}\n") } } - } - return """ -SeleniumSession - Create and manage a stateful Selenium browser session - * Specify the URL to navigate to - * Provide JavaScript commands to execute in sequence through Selenium's executeScript method - * Can be used for web scraping, testing, or automation - * Session persists between commands for stateful interactions - * Optionally specify sessionId to reuse an existing session - * Set closeSession=true to close the session after execution -Example JavaScript Commands: - * "return document.title;" - Get page title - * "return document.querySelector('.my-class').textContent;" - Get element text - * "return Array.from(document.querySelectorAll('a')).map(a => a.href);" - Get all links - * "document.querySelector('#my-button').click();" - Click an element - * "window.scrollTo(0, document.body.scrollHeight);" - Scroll to bottom - * "return document.documentElement.outerHTML;" - Get entire page HTML - * "return new Promise(r => setTimeout(() => r(document.title), 1000));" - Async operation -Note: Commands are executed in the browser context and must be valid JavaScript. - Use proper error handling and waits for dynamic content. - -Active Sessions: -$activeSessionsInfo -""".trimMargin() - } override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/WebFetchAndTransformTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/WebFetchAndTransformTask.kt index 4e767a53..c69d5226 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/WebFetchAndTransformTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/WebFetchAndTransformTask.kt @@ -39,7 +39,7 @@ open class WebFetchAndTransformTask( WebFetchAndTransform - Fetch a web page, strip HTML, and transform content ** Specify the URL to fetch ** Specify the desired format or focus for the transformation - """.trimMargin() + """.trimIndent() override fun run( agent: PlanCoordinator, @@ -81,11 +81,7 @@ open class WebFetchAndTransformTask( model = planSettings.defaultModel, ).answer( listOf( - """ - |Transform the following web content according to this goal: $transformationGoal - | - |$content - """.trimMargin(), + "Transform the following web content according to this goal: $transformationGoal\n\n$content", ), api ) } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/AbstractFileTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/AbstractFileTask.kt index 18b150e1..dd0ccc0b 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/AbstractFileTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/AbstractFileTask.kt @@ -50,13 +50,7 @@ abstract class AbstractFileTask( .joinToString("\n\n") { relativePath -> val file = root.resolve(relativePath).toFile() try { - """ - |# $relativePath - | - |$TRIPLE_TILDE - |${codeFiles[file.toPath()] ?: file.readText()} - |$TRIPLE_TILDE - """.trimMargin() + "# $relativePath\n\n$TRIPLE_TILDE\n${codeFiles[file.toPath()] ?: file.readText()}\n$TRIPLE_TILDE" } catch (e: Throwable) { log.warn("Error reading file: $relativePath", e) "" diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeOptimizationTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeOptimizationTask.kt index aef85d69..ae3793a2 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeOptimizationTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeOptimizationTask.kt @@ -30,34 +30,30 @@ class CodeOptimizationTask( override val actorName = "CodeOptimization" override val actorPrompt = """ - Analyze the provided code and suggest optimizations to improve code quality. Focus exclusively on: - |1. Code structure and organization - |2. Readability improvements - |3. Maintainability enhancements - |4. Proper use of language-specific features and best practices - |5. Design pattern applications - | - |Provide detailed explanations for each suggested optimization, including: - |- The reason for the optimization - |- The expected benefits - |- Any potential trade-offs or considerations - | - |Format the response as a markdown document with appropriate headings and code snippets. - |Use diff format to show the proposed changes clearly. - """.trimMargin() - - override fun promptSegment(): String { - return """ - |CodeOptimization - Analyze and optimize existing code for better readability, maintainability, and adherence to best practices - | * Specify the files to be optimized - | * Optionally provide specific areas of focus for the optimization (e.g., code structure, readability, design patterns) - """.trimMargin() - } - - - override fun getAnalysisInstruction(): String { - return "Optimize the following code for better readability and maintainability" - } + Analyze the provided code and suggest optimizations to improve code quality. Focus exclusively on: + 1. Code structure and organization + 2. Readability improvements + 3. Maintainability enhancements + 4. Proper use of language-specific features and best practices + 5. Design pattern applications + + Provide detailed explanations for each suggested optimization, including: + - The reason for the optimization + - The expected benefits + - Any potential trade-offs or considerations + + Format the response as a markdown document with appropriate headings and code snippets. + Use diff format to show the proposed changes clearly. + """.trimIndent() + + override fun promptSegment() = """ + CodeOptimization - Analyze and optimize existing code for better readability, maintainability, and adherence to best practices + * Specify the files to be optimized + * Optionally provide specific areas of focus for the optimization (e.g., code structure, readability, design patterns) + """.trimIndent() + + + override fun getAnalysisInstruction() = "Optimize the following code for better readability and maintainability" companion object { private val log = LoggerFactory.getLogger(CodeOptimizationTask::class.java) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeReviewTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeReviewTask.kt index 466ca715..e3c7ef57 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeReviewTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/CodeReviewTask.kt @@ -48,13 +48,11 @@ class CodeReviewTask( if (focusAreas != null) ". Focus on these areas: $focusAreas" else "" } - override fun promptSegment(): String { - return """ - CodeReview - Perform an automated code review and provide suggestions for improvements - ** Specify the files to be reviewed - ** Optionally provide specific areas of focus for the review - """.trimMargin() - } + override fun promptSegment() = """ + CodeReview - Perform an automated code review and provide suggestions for improvements + ** Specify the files to be reviewed + ** Optionally provide specific areas of focus for the review + """.trimIndent() companion object { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/DocumentationTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/DocumentationTask.kt index 7b78e034..c5d4322c 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/DocumentationTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/DocumentationTask.kt @@ -36,32 +36,30 @@ class DocumentationTask( state = state ) - override fun promptSegment(): String { - return """ - Documentation - Generate documentation - ** List input file names and tasks to be examined - ** List topics to document - ** List output files to be modified or created with documentation - """.trimMargin() - } + override fun promptSegment() = """ + Documentation - Generate documentation + ** List input file names and tasks to be examined + ** List topics to document + ** List output files to be modified or created with documentation + """.trimIndent() val documentationGeneratorActor by lazy { SimpleActor( name = "DocumentationGenerator", prompt = """ - Create detailed and clear documentation for the provided code, covering its purpose, functionality, inputs, outputs, and any assumptions or limitations. - Use a structured and consistent format that facilitates easy understanding and navigation. - Include code examples where applicable, and explain the rationale behind key design decisions and algorithm choices. - Document any known issues or areas for improvement, providing guidance for future developers on how to extend or maintain the code. - For existing files, provide documentation in the form of comments within the code. - For new files, create separate markdown files with the documentation. - Response format: - For existing files: Use ${TRIPLE_TILDE}diff code blocks with a header specifying the file path. - For new files: Use $TRIPLE_TILDE markdown blocks with a header specifying the new file path. - The diff format should use + for line additions, - for line deletions. - Include 2 lines of context before and after every change in diffs. - Separate code blocks with a single blank line. - """.trimMargin(), + Create detailed and clear documentation for the provided code, covering its purpose, functionality, inputs, outputs, and any assumptions or limitations. + Use a structured and consistent format that facilitates easy understanding and navigation. + Include code examples where applicable, and explain the rationale behind key design decisions and algorithm choices. + Document any known issues or areas for improvement, providing guidance for future developers on how to extend or maintain the code. + For existing files, provide documentation in the form of comments within the code. + For new files, create separate markdown files with the documentation. + Response format: + For existing files: Use ${TRIPLE_TILDE}diff code blocks with a header specifying the file path. + For new files: Use $TRIPLE_TILDE markdown blocks with a header specifying the new file path. + The diff format should use + for line additions, - for line deletions. + Include 2 lines of context before and after every change in diffs. + Separate code blocks with a single blank line. + """.trimIndent(), model = planSettings.getTaskSettings(TaskType.Documentation).model ?: planSettings.defaultModel, temperature = planSettings.temperature, ) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/FileModificationTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/FileModificationTask.kt index 937aeebf..e296dc3e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/FileModificationTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/FileModificationTask.kt @@ -40,68 +40,66 @@ class FileModificationTask( SimpleActor( name = "FileModification", prompt = """ - Generate precise code modifications and new files based on requirements: - For modifying existing files: - - Write efficient, readable, and maintainable code changes - - Ensure modifications integrate smoothly with existing code - - Follow project coding standards and patterns - - Consider dependencies and potential side effects - - Provide clear context and rationale for changes - - For creating new files: - - Choose appropriate file locations and names - - Structure code according to project conventions - - Include necessary imports and dependencies - - Add comprehensive documentation - - Ensure no duplication of existing functionality - - Provide a clear summary explaining: - - What changes were made and why - - Any important implementation details - - Potential impacts on other code - - Required follow-up actions - - Response format: - For existing files: Use ${TRIPLE_TILDE}diff code blocks with a header specifying the file path. - For new files: Use $TRIPLE_TILDE code blocks with a header specifying the new file path. - The diff format should use + for line additions, - for line deletions. - Include 2 lines of context before and after every change in diffs. - Separate code blocks with a single blank line. - For new files, specify the language for syntax highlighting after the opening triple backticks. - - Example: - - Here are the modifications: - - ### src/utils/existingFile.js - ${TRIPLE_TILDE}diff - // Existing utility functions - function existingFunction() { - return 'old result'; - return 'new result'; - | } - $TRIPLE_TILDE - - ### src/utils/newFile.js - ${TRIPLE_TILDE}js - // New utility functions - function newFunction() { - return 'new functionality'; - |} - $TRIPLE_TILDE - """.trimMargin(), + Generate precise code modifications and new files based on requirements: + For modifying existing files: + - Write efficient, readable, and maintainable code changes + - Ensure modifications integrate smoothly with existing code + - Follow project coding standards and patterns + - Consider dependencies and potential side effects + - Provide clear context and rationale for changes + + For creating new files: + - Choose appropriate file locations and names + - Structure code according to project conventions + - Include necessary imports and dependencies + - Add comprehensive documentation + - Ensure no duplication of existing functionality + + Provide a clear summary explaining: + - What changes were made and why + - Any important implementation details + - Potential impacts on other code + - Required follow-up actions + + Response format: + For existing files: Use ${TRIPLE_TILDE}diff code blocks with a header specifying the file path. + For new files: Use $TRIPLE_TILDE code blocks with a header specifying the new file path. + The diff format should use + for line additions, - for line deletions. + Include 2 lines of context before and after every change in diffs. + Separate code blocks with a single blank line. + For new files, specify the language for syntax highlighting after the opening triple backticks. + + Example: + + Here are the modifications: + + ### src/utils/existingFile.js + ${TRIPLE_TILDE}diff + // Existing utility functions + function existingFunction() { + return 'old result'; + return 'new result'; + } + $TRIPLE_TILDE + + ### src/utils/newFile.js + ${TRIPLE_TILDE}js + // New utility functions + function newFunction() { + return 'new functionality'; + } + $TRIPLE_TILDE + """.trimIndent(), model = planSettings.getTaskSettings(TaskType.FileModification).model ?: planSettings.defaultModel, temperature = planSettings.temperature, ) } - override fun promptSegment(): String { - return """ - FileModification - Modify existing files or create new files - ** For each file, specify the relative file path and the goal of the modification or creation - ** List input files/tasks to be examined when designing the modifications or new files - """.trimMargin() - } + override fun promptSegment() = """ + FileModification - Modify existing files or create new files + ** For each file, specify the relative file path and the goal of the modification or creation + ** List input files/tasks to be examined when designing the modifications or new files + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/InquiryTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/InquiryTask.kt index a274472d..8a8f073e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/InquiryTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/InquiryTask.kt @@ -9,6 +9,7 @@ import com.simiacryptus.jopenai.models.ApiModel.Role import com.simiacryptus.jopenai.util.ClientUtil.toContentList import com.simiacryptus.skyenet.Discussable import com.simiacryptus.skyenet.apps.plan.* +import com.simiacryptus.skyenet.apps.plan.tools.file.AbstractFileTask.Companion.TRIPLE_TILDE import com.simiacryptus.skyenet.core.actors.SimpleActor import com.simiacryptus.skyenet.util.MarkdownUtil import com.simiacryptus.skyenet.webui.session.SessionTask @@ -42,14 +43,14 @@ class InquiryTask( ) override fun promptSegment() = if (planSettings.allowBlocking) """ - |Inquiry - Answer questions by reading in files and providing a summary that can be discussed with and approved by the user - | ** Specify the questions and the goal of the inquiry - | ** List input files to be examined when answering the questions - """.trimMargin() else """ - |Inquiry - Answer questions by reading in files and providing a report - | ** Specify the questions and the goal of the inquiry - | ** List input files to be examined when answering the questions - """.trimMargin() + Inquiry - Answer questions by reading in files and providing a summary that can be discussed with and approved by the user + ** Specify the questions and the goal of the inquiry + ** List input files to be examined when answering the questions + """.trimIndent() else """ + Inquiry - Answer questions by reading in files and providing a report + ** Specify the questions and the goal of the inquiry + ** List input files to be examined when answering the questions + """.trimIndent() private val inquiryActor by lazy { SimpleActor( @@ -63,10 +64,10 @@ class InquiryTask( When generating insights, consider the existing project context and focus on information that is directly relevant and applicable. Focus on generating insights and information that support the task types available in the system (${ - planSettings.taskSettings.filter { it.value.enabled }.keys.joinToString(", ") - }). + planSettings.taskSettings.filter { it.value.enabled }.keys.joinToString(", ") + }). This will ensure that the inquiries are tailored to assist in the planning and execution of tasks within the system's framework. - """.trimMargin(), + """.trimIndent(), model = planSettings.getTaskSettings(TaskType.valueOf(planTask?.task_type!!)).model ?: planSettings.defaultModel, temperature = planSettings.temperature, ) @@ -152,13 +153,7 @@ class InquiryTask( .joinToString("\n\n") { relativePath -> val file = root.resolve(relativePath).toFile() try { - """ - |# $relativePath - | - |${AbstractFileTask.TRIPLE_TILDE} - |${codeFiles[file.toPath()] ?: file.readText()} - |${AbstractFileTask.TRIPLE_TILDE} - """.trimMargin() + "# $relativePath\n\n$TRIPLE_TILDE\n${codeFiles[file.toPath()] ?: file.readText()}\n$TRIPLE_TILDE" } catch (e: Throwable) { log.warn("Error reading file: $relativePath", e) "" diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/PerformanceAnalysisTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/PerformanceAnalysisTask.kt index 804ca928..6a375fbe 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/PerformanceAnalysisTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/PerformanceAnalysisTask.kt @@ -29,29 +29,27 @@ class PerformanceAnalysisTask( override val actorName = "PerformanceAnalysis" override val actorPrompt = """ -Analyze the provided code for performance issues and bottlenecks. Focus exclusively on: -1. Time complexity of algorithms -2. Memory usage and potential leaks -3. I/O operations and network calls -4. Concurrency and parallelism opportunities -5. Caching and memoization possibilities - -Provide detailed explanations for each identified performance issue, including: - The reason it's a performance concern - The potential impact on system performance -- Quantitative estimates of performance impact where possible - -Format the response as a markdown document with appropriate headings and code snippets. -Do not provide code changes, focus on analysis and recommendations. + Analyze the provided code for performance issues and bottlenecks. Focus exclusively on: + 1. Time complexity of algorithms + 2. Memory usage and potential leaks + 3. I/O operations and network calls + 4. Concurrency and parallelism opportunities + 5. Caching and memoization possibilities + + Provide detailed explanations for each identified performance issue, including: + The reason it's a performance concern + The potential impact on system performance + Quantitative estimates of performance impact where possible + + Format the response as a markdown document with appropriate headings and code snippets. + Do not provide code changes, focus on analysis and recommendations. """.trimIndent() - override fun promptSegment(): String { - return """ -PerformanceAnalysis - Analyze code for performance issues and suggest improvements - ** Specify the files to be analyzed - ** Optionally provide specific areas of focus for the analysis (e.g., time complexity, memory usage, I/O operations) - """.trimMargin() - } + override fun promptSegment()= """ + PerformanceAnalysis - Analyze code for performance issues and suggest improvements + ** Specify the files to be analyzed + ** Optionally provide specific areas of focus for the analysis (e.g., time complexity, memory usage, I/O operations) + """.trimIndent() fun getFiles(): List { return taskConfig?.input_files ?: emptyList() diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/RefactorTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/RefactorTask.kt index c6fea477..8573dc1e 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/RefactorTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/RefactorTask.kt @@ -48,13 +48,11 @@ Use diff format to show the proposed changes clearly. override fun getAnalysisInstruction(): String = "Refactor the following code" - override fun promptSegment(): String { - return """ -RefactorTask - Analyze and refactor existing code to improve structure, readability, and maintainability - ** Specify the files to be refactored - ** Optionally provide specific areas of focus for the refactoring (e.g., modularity, design patterns, naming conventions) - """.trimMargin() - } + override fun promptSegment() = """ + RefactorTask - Analyze and refactor existing code to improve structure, readability, and maintainability + ** Specify the files to be refactored + ** Optionally provide specific areas of focus for the refactoring (e.g., modularity, design patterns, naming conventions) + """.trimIndent() companion object { private val log = LoggerFactory.getLogger(RefactorTask::class.java) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/SecurityAuditTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/SecurityAuditTask.kt index af64d21c..c169094a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/SecurityAuditTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/file/SecurityAuditTask.kt @@ -46,13 +46,11 @@ Use diff format to show the proposed security fixes clearly. override fun getAnalysisInstruction(): String = "Perform a security audit on the following code" - override fun promptSegment(): String { - return """ -SecurityAudit - Perform an automated security audit and provide suggestions for improving code security - ** Specify the files to be audited - ** Optionally provide specific areas of focus for the security audit - """.trimMargin() - } + override fun promptSegment() = """ + SecurityAudit - Perform an automated security audit and provide suggestions for improving code security + ** Specify the files to be audited + ** Optionally provide specific areas of focus for the security audit + """.trimIndent() companion object { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/knowledge/WebSearchAndIndexTask.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/knowledge/WebSearchAndIndexTask.kt index 95d99e2a..6d6ddde5 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/knowledge/WebSearchAndIndexTask.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/tools/knowledge/WebSearchAndIndexTask.kt @@ -43,11 +43,11 @@ class WebSearchAndIndexTask( ) override fun promptSegment() = """ - WebSearchAndIndex - Search web, download content, parse and index for future embedding search - ** Specify the search query - ** Specify number of results to process (max 10) - ** Specify output directory for indexed content - """.trimMargin() + WebSearchAndIndex - Search web, download content, parse and index for future embedding search + ** Specify the search query + ** Specify number of results to process (max 10) + ** Specify output directory for indexed content + """.trimIndent() override fun run( agent: PlanCoordinator, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/interpreter/ProcessInterpreter.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/interpreter/ProcessInterpreter.kt index 09138ef9..838961dc 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/interpreter/ProcessInterpreter.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/interpreter/ProcessInterpreter.kt @@ -43,19 +43,7 @@ open class ProcessInterpreter( throw RuntimeException("Timeout; output: $output; error: $error") } else if (error.isNotEmpty()) { //throw RuntimeException(error) - return ( - """ - |ERROR: - |```text - |$error - |``` - | - |OUTPUT: - |```text - |$output - |``` - """.trimMargin() - ) + return "ERROR:\n```text\n$error\n```\n\nOUTPUT:\n```text\n$output\n```" } else { return output } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/util/MarkdownUtil.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/util/MarkdownUtil.kt index 4d9bd231..5f9a1fb1 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/util/MarkdownUtil.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/util/MarkdownUtil.kt @@ -78,17 +78,15 @@ ${Thread.currentThread().stackTrace.joinToString("\n") { "
  • " + it.toString() log.warn("Failed to render Mermaid diagram", e) } val replacement = if (tabs) """ - |
    - |
    - | - | - |
    - |
    $mermaidDiagramHTML
    - |
    $fixedMermaidCode
    - |
    - |""".trimMargin() else """ - |$mermaidDiagramHTML - |""".trimMargin() +
    +
    + + +
    +
    """.trimIndent() + mermaidDiagramHTML + """
    +
    """.trimIndent() + fixedMermaidCode + """
    +
    + """.trimIndent() else mermaidDiagramHTML htmlContent = htmlContent.replace(match.value, replacement) } return htmlContent diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/CancelThreadsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/CancelThreadsServlet.kt index 4ead063a..11ed9d88 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/CancelThreadsServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/CancelThreadsServlet.kt @@ -21,22 +21,21 @@ class CancelThreadsServlet( if (req.parameterMap.containsKey("sessionId")) { val session = Session(req.getParameter("sessionId")) //language=HTML - resp.writer.write( - """ - | - | - | Cancel Session - | - | - | - |
    - | - | CONFIRM: - | - |
    - | - | - """.trimMargin() + resp.writer.write(""" + + + Cancel Session + + + +
    + + CONFIRM: + +
    + + + """.trimIndent() ) } else { resp.status = HttpServletResponse.SC_BAD_REQUEST diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt index eb11d1e5..725f898f 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt @@ -21,20 +21,20 @@ class DeleteSessionServlet( //language=HTML resp.writer.write( """ - | - | - | Delete Session - | - | - | - |
    - | - | CONFIRM: - | - |
    - | - | - """.trimMargin() + + + Delete Session + + + +
    + + CONFIRM: + +
    + + + """.trimIndent() ) } else { resp.status = HttpServletResponse.SC_BAD_REQUEST diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt index 7b879435..0af8c461 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt @@ -86,7 +86,7 @@ abstract class FileServlet : HttpServlet() { getZipLink(req, pathSegments.drop(1).joinToString("/")), folders, files - ).trimMargin() + ) ) } } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionSettingsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionSettingsServlet.kt index 2b725581..6226bfbc 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionSettingsServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionSettingsServlet.kt @@ -27,24 +27,22 @@ class SessionSettingsServlet( return } //language=HTML - resp.writer.write( - """ - | - | - | Settings - | - | - | - |
    - | - | - | - | - |
    - | - | - """.trimMargin() - ) + resp.writer.write(""" + + + Settings + + + +
    + + + + +
    + + + """.trimIndent()) } else { resp.status = HttpServletResponse.SC_BAD_REQUEST resp.writer.write("Session ID is required") diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionShareServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionShareServlet.kt index aec61a3a..f1f3d545 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionShareServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionShareServlet.kt @@ -98,22 +98,23 @@ class SessionShareServlet( resp.status = HttpServletResponse.SC_OK //language=HTML resp.writer.write( - """ - | - | Save Session - | - | - | - | - |

    Sharing URL

    - |

    shareURL

    - | - |

    Sharing URL

    - |

    $shareURL

    - | QR Code for $shareURL - | - |""".trimMargin() + """ + + + Save Session + + + + +

    Sharing URL

    +

    shareURL

    + +

    Sharing URL

    +

    $shareURL

    + QR Code for $shareURL + + """.trimIndent() ) } @@ -152,23 +153,22 @@ class SessionShareServlet( //language=HTML shareURL = url(appName, shareId) qrCodeDataURL = generateQRCodeDataURL(shareURL) - resp.writer.write( - """ - | - | - | Saving Session - | - | - | - | - |

    Saving Session... This page will soon be ready!

    - |

    $shareURL

    - | QR Code for $shareURL - |

    To monitor progress, you can use the session threads page

    - | - | - """.trimMargin() + resp.writer.write(""" + + + Saving Session + + + + +

    Saving Session... This page will soon be ready!

    +

    $shareURL

    + QR Code for $shareURL +

    To monitor progress, you can use the session threads page

    + + + """.trimIndent() ) } } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionThreadsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionThreadsServlet.kt index 8acf13c1..2533098f 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionThreadsServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionThreadsServlet.kt @@ -24,101 +24,98 @@ class SessionThreadsServlet( //language=HTML resp.writer.write( """ - | - | - | Session Threads - | - | - | - | - | - |
    - |

    Pool Stats

    - |

    Session: $session

    - |

    User: $user

    - |

    Pool: $pool

    - |

    Threads: ${pool.activeCount}/${pool.poolSize}

    - |

    Queue: ${pool.queue.size}/${pool.queue.remainingCapacity()}

    - |

    Completed: ${pool.completedTaskCount}

    - |

    Task Count: ${pool.taskCount}

    - |
    - |
    - |

    Thread Stacks

    - |${ - (pool.threadFactory as RecordingThreadFactory).threads.filter { it.isAlive } - .joinToString("""
    """) { thread -> - """ -
    -
    ${thread.name}
    -
    ${ - thread.stackTrace.joinToString(separator = "\n") - { stackTraceElement -> """
    $stackTraceElement
    """ } - }
    -
    - """ - } - } - |
    - | - | - """.trimMargin() + + + Session Threads + + + + + +
    +

    Pool Stats

    +

    Session: """.trimIndent() + session + """

    +

    User: """.trimIndent() + user + """

    +

    Pool: """.trimIndent() + pool + """

    +

    Threads: """.trimIndent() + pool.activeCount + "/" + pool.poolSize + """

    +

    Queue: """.trimIndent() + pool.queue.size + "/" + pool.queue.remainingCapacity() + """

    +

    Completed: """.trimIndent() + pool.completedTaskCount + """

    +

    Task Count: """.trimIndent() + pool.taskCount + """

    +
    +
    +

    Thread Stacks

    + """.trimIndent() + (pool.threadFactory as RecordingThreadFactory).threads.filter { it.isAlive } + .joinToString("
    ") { thread -> + """ +
    +
    ${thread.name}
    +
    ${ + thread.stackTrace.joinToString(separator = "\n") + { stackTraceElement -> "
    $stackTraceElement
    " } + }
    +
    + """.trimIndent() + } + """ +
    + + + """.trimIndent() ) - // (pool.threadFactory as RecordingThreadFactory).threads } else { resp.status = HttpServletResponse.SC_BAD_REQUEST resp.writer.write("Session ID is required") diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt index ab2e6e22..e4dbb200 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt @@ -41,37 +41,37 @@ class UserSettingsServlet : HttpServlet() { //language=HTML resp.writer.write( """ - | - | - | Settings - | - | - | - |
    - | - | - | - |
    - | - | - """.trimMargin() + + + Settings + + + +
    + + + +
    + + + """.trimIndent() ) } catch (e: Exception) { resp.status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR // HTML error page resp.writer.write( - """ - | - | - | Error - | - | - | - |

    Error

    - |
    ${e.message}
    - | - | - """.trimIndent() + """ + + + Error + + + +

    Error

    +
    """.trimIndent() + e.message + """
    + + + """.trimIndent() ) resp } 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 c0dae8b8..ce26743c 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 @@ -126,47 +126,23 @@ abstract class SessionTask( ) = hideable( ui, when { - e is ValidatedObject.ValidationError -> renderMarkdown( - """ - |**Data Validation Error** - | - |${e.message} - | - |Stack Trace: - |```text - |${e.stackTraceTxt/*.indent(" ")*/} - |``` - | - |""".trimMargin(), ui = ui - ) + e is ValidatedObject.ValidationError -> renderMarkdown(""" + **Data Validation Error** + + """.trimIndent() + e.message + """ + + Stack Trace: + ```text + """.trimIndent() + e.stackTraceTxt + """ + ``` + """, ui = ui) e is CodingActor.FailedToImplementException -> renderMarkdown( - """ - |**Failed to Implement** - | - |${e.message} - | - |Prefix: - |```${e.language?.lowercase() ?: ""} - |${/*escapeHtml4*/(e.prefix/*?.indent(" ")*/ ?: "")} - |``` - | - |Implementation Attempt: - |```${e.language?.lowercase() ?: ""} - |${/*escapeHtml4*/(e.code/*?.indent(" ")*/ ?: "")} - |``` - | - |""".trimMargin(), ui = ui + "**Failed to Implement** \n\n${e.message}\n\nPrefix:\n```${e.language?.lowercase() ?: ""}\n${e.prefix}\n```\n\nImplementation Attempt:\n```${e.language?.lowercase() ?: ""}\n${e.code}\n```\n\n", ui = ui ) else -> renderMarkdown( - """ - |**Error `${e.javaClass.name}`** - | - |```text - |${e.stackTraceToString().let { /*escapeHtml4*/(it)/*.indent(" ")*/ }} - |``` - |""".trimMargin(), ui = ui + "**Error `${e.javaClass.name}`**\n\n```text\n${e.stackTraceToString()}\n```\n", ui = ui ) }, showSpinner, tag, "error" ) 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 8e974e52..b2ccffda 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 @@ -23,42 +23,26 @@ open class CodingActorTestApp( path = "/codingActorTest", ) { override fun userMessage( - session: Session, - user: User?, - userMessage: String, - ui: ApplicationInterface, - api: API + session: Session, user: User?, userMessage: String, ui: ApplicationInterface, api: API ) { (api as ChatClient).budget = 2.00 val message = ui.newTask() try { 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, - user, - OperationType.Execute - ) + val canPlay = ApplicationServices.authorizationManager.isAuthorized(this::class.java, user, OperationType.Execute) val playLink = if (!canPlay) "" else { ui.hrefLink("▶", "href-link play-button") { message.add("Running...") val result = response.result message.complete( - """ - |
    ${result.resultValue}
    - |
    ${result.resultOutput}
    - """.trimMargin() + "
    ${result.resultValue}
    \n
    ${result.resultOutput}
    " ) } } message.complete( renderMarkdown( - """ - |```${actor.language.lowercase(Locale.getDefault())} - |${/*escapeHtml4*/(response.code)/*.indent(" ")*/} - |``` - |$playLink - """.trimMargin().trim(), ui = ui + "```${actor.language.lowercase(Locale.getDefault())}\n${response.code}\n```\n$playLink".trim(), ui = ui ) ) } catch (e: Throwable) { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/FilePatchTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/FilePatchTestApp.kt index af5ef569..aa5b8e25 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/FilePatchTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/FilePatchTestApp.kt @@ -25,24 +25,24 @@ open class FilePatchTestApp( val task = ui.newTask(true) val source = """ - |fun main(args: Array) { - | println(${'"'}"" - | Hello, World! - | ${'"'}"") - |} - """.trimMargin() + fun main(args: Array) { + println(${'"'}"" + Hello, World! + ${'"'}"") + } + """.trimIndent() val sourceFile = Files.createTempFile("source", ".txt").toFile() sourceFile.writeText(source) sourceFile.deleteOnExit() val patch = """ - |# ${sourceFile.name} - | - |```diff - |-Hello, World! - |+Goodbye, World! - |``` - """.trimMargin() + # ${sourceFile.name} + + ```diff + -Hello, World! + +Goodbye, World! + ``` + """.trimIndent() val newPatch = socketManager.addApplyFileDiffLinks( root = sourceFile.toPath().parent, response = patch, 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 3d0fc4bd..0458fd5f 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 @@ -33,12 +33,7 @@ open class ParsedActorTestApp( val response = actor.answer(listOf(userMessage), api = api) message.complete( renderMarkdown( - """ - |${response.text} - |``` - |${JsonUtil.toJson(response.obj)} - |``` - """.trimMargin().trim(), ui = ui + "${response.text}\n```\n${JsonUtil.toJson(response.obj)}\n```".trim(), ui = ui ) ) } catch (e: Throwable) { diff --git a/webui/src/test/kotlin/com/simiacryptus/diff/DiffMatchPatchTest.kt b/webui/src/test/kotlin/com/simiacryptus/diff/DiffMatchPatchTest.kt new file mode 100644 index 00000000..84b34182 --- /dev/null +++ b/webui/src/test/kotlin/com/simiacryptus/diff/DiffMatchPatchTest.kt @@ -0,0 +1,126 @@ +package com.simiacryptus.diff + +import com.simiacryptus.diff.DiffMatchPatch.Diff +import com.simiacryptus.diff.DiffMatchPatch.Operation.* +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.util.* + + +class DiffMatchPatchTest { + + @Test + fun testDiffMain() { + // Test equality + Assertions.assertEquals(mutableListOf(Diff(EQUAL, "test")), DiffMatchPatch.diff_main("test", "test", false)) + + // Test differences + val diffs = LinkedList() + diffs.add(Diff(DELETE, "Hello")) + diffs.add(Diff(INSERT, "Goodbye")) + diffs.add(Diff(EQUAL, " world.")) + Assertions.assertEquals( + mutableListOf( + Diff(DELETE, "Hell"), Diff(INSERT, "G"), Diff(EQUAL, "o"), Diff(INSERT, "odbye"), Diff(EQUAL, " world.") + ), DiffMatchPatch.diff_main("Hello world.", "Goodbye world.", false) + ) + } + + @Test + fun testDiffCommonPrefix() { + Assertions.assertEquals(0, DiffMatchPatch.diff_commonPrefix("abc", "xyz")) + Assertions.assertEquals(4, DiffMatchPatch.diff_commonPrefix("1234abcdef", "1234xyz")) + Assertions.assertEquals(4, DiffMatchPatch.diff_commonPrefix("1234", "1234xyz")) + } + + @Test + fun testDiffCommonSuffix() { + Assertions.assertEquals(0, DiffMatchPatch.diff_commonSuffix("abc", "xyz")) + Assertions.assertEquals(4, DiffMatchPatch.diff_commonSuffix("abcdef1234", "xyz1234")) + Assertions.assertEquals(4, DiffMatchPatch.diff_commonSuffix("1234", "xyz1234")) + } + + // @Test + fun testPatchMakeAndApply() { + val text1 = "The quick brown fox jumps over the lazy dog." + val text2 = "The quick red fox jumps over the tired dog." + val patches: LinkedList = DiffMatchPatch.patch_make(text1, text2) + val results: Array = DiffMatchPatch.patch_apply(patches, text1) + + Assertions.assertEquals(text2, results[0]) + val applied = results[1] as BooleanArray + for (b in applied) { + Assertions.assertTrue(b) + } + } + + // @Test + fun testPatchMakeWithDiffs() { + val text1 = "The quick brown fox jumps over the lazy dog." + val text2 = "That quick brown fox jumped over a lazy dog." + val diffs: LinkedList = DiffMatchPatch.diff_main(text1, text2, false) + val patches: LinkedList = DiffMatchPatch.patch_make(diffs) + + Assertions.assertFalse(patches.isEmpty()) + Assertions.assertEquals(diffs, patches.first().diffs) + } + + @Test + fun testPatchToText() { + val text1 = "The quick brown\n fox jumps over\n the lazy dog.\n" + val text2 = "The quick red\n fox jumps over\n the tired dog.\n" + if (text1 == null || text2 == null) { + throw IllegalArgumentException("Null inputs. (patch_make)") + } + // No diffs provided, compute our own. + // Set a deadline by which time the diff must be complete. + val deadline: Long + if (DiffMatchPatch.Diff_Timeout <= 0) { + deadline = Long.MAX_VALUE + } else { + deadline = System.currentTimeMillis() + (DiffMatchPatch.Diff_Timeout * 1000).toLong() + } + val diffs = DiffMatchPatch.diff_main(text1, text2, false /*true*/, deadline) + if (diffs.size > 2) { + DiffMatchPatch.diff_cleanupSemantic(diffs) + DiffMatchPatch.diff_cleanupEfficiency(diffs) + } + val patches: LinkedList = DiffMatchPatch.patch_make(text1, diffs) + val patchText: String = DiffMatchPatch.patch_toText(patches) + println(patchText) + + Assertions.assertFalse(patchText.isEmpty()) + Assertions.assertTrue(patchText.startsWith("@@ -1,"), patchText) + } + + @Test + fun testPatchFromText() { + val patchText = """@@ -1,8 +1,5 @@ +-The quick ++That + brown +@@ -22,17 +19,17 @@ + jumps +-over the lazy ++over a lazy + dog. +""" + val patches = DiffMatchPatch.patch_fromText(patchText) + + Assertions.assertFalse(patches.isEmpty()) + Assertions.assertEquals("The quick", patches.first().diffs.first().text) + } + + @Test + // expect IllegalArgumentException::class + fun testPatchFromTextWithInvalidInput() { + val invalidPatchText = """@@ -1,8 +1,5 @@ +-The quick ++That + brown +""" + // This should throw an IllegalArgumentException due to the incomplete patch text + DiffMatchPatch.patch_fromText(invalidPatchText) + } + +} diff --git a/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt b/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt index f2c14ae0..abb3140c 100644 --- a/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt +++ b/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt @@ -129,13 +129,13 @@ class IterativePatchUtilTest { line3 """.trimIndent() val expected = """ - |line1 - | lineA - | lineB - | - |line2 - |line3 - """.trimMargin() + line1 + lineA + lineB + + line2 + line3 + """.trimIndent() val result = IterativePatchUtil.applyPatch(source, patch) assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n")) } @@ -158,13 +158,13 @@ class IterativePatchUtilTest { line3 """.trimIndent() val expected = """ - |line1 - | lineA - | lineB - | - |line2 - |line3 - """.trimMargin() + line1 + lineA + lineB + + line2 + line3 + """.trimIndent() val result = IterativePatchUtil.applyPatch(source, patch) assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n")) } @@ -275,17 +275,17 @@ class IterativePatchUtilTest { // Similar changes for black pawns """.trimIndent() val patch = """ - | export class StandardChessModel implements GameModel { - | // ... other methods ... - | - |- getWinner(): 'white' | 'black' | 'draw' | null { - |+ getWinner(): ChessColor | 'draw' | null { - | return null; - | } - | - | // ... other methods ... - | } - """.trimMargin() + export class StandardChessModel implements GameModel { + // ... other methods ... + + - getWinner(): 'white' | 'black' | 'draw' | null { + + getWinner(): ChessColor | 'draw' | null { + return null; + } + + // ... other methods ... + } + """.trimIndent() val expected = """ export class StandardChessModel implements GameModel { geometry: BoardGeometry; @@ -332,80 +332,37 @@ class IterativePatchUtilTest { @Test fun testGeneratePatchNoChanges() { - val oldCode = """ - |line1 - |line2 - |line3 - """.trimMargin() + val oldCode = "line1\nline2\nline3" val newCode = oldCode val result = IterativePatchUtil.generatePatch(oldCode, newCode) - val expected = """ - """.trimMargin() + val expected = "" assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n")) } @Test fun testGeneratePatchAddLine() { - val oldCode = """ - |line1 - |line2 - |line3 - """.trimMargin() - val newCode = """ - |line1 - |line2 - |newLine - |line3 - """.trimMargin() + val oldCode = "line1\nline2\nline3" + val newCode = "line1\nline2\nnewLine\nline3" val result = IterativePatchUtil.generatePatch(oldCode, newCode) - val expected = """ - | line1 - | line2 - |+ newLine - | line3 - """.trimMargin() + val expected = " line1\n line2\n+ newLine\n line3" assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n")) } @Test fun testGeneratePatchRemoveLine() { - val oldCode = """ - line1 - line2 - line3 - """.trimIndent() - val newCode = """ - line1 - line3 - """.trimIndent() + val oldCode = "line1\nline2\nline3" + val newCode = "line1\nline3" val result = IterativePatchUtil.generatePatch(oldCode, newCode) - val expected = """ - | line1 - |- line2 - | line3 - """.trimMargin() + val expected = " line1\n- line2\n line3" assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n")) } @Test fun testGeneratePatchModifyLine() { - val oldCode = """ - line1 - line2 - line3 - """.trimIndent() - val newCode = """ - line1 - modifiedLine2 - line3 - """.trimIndent() + val oldCode = "line1\nline2\nline3" + val newCode = "line1\nmodifiedLine2\nline3" val result = IterativePatchUtil.generatePatch(oldCode, newCode) - val expected = """ - | line1 - |- line2 - |+ modifiedLine2 - | line3 - """.trimMargin() + val expected = " line1\n- line2\n+ modifiedLine2\n line3" assertEquals( expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n") @@ -431,16 +388,16 @@ class IterativePatchUtilTest { """.trimIndent() val result = IterativePatchUtil.generatePatch(oldCode, newCode) val expected = """ - | function example() { - |- console.log("Hello"); - |- // Some comment - |- return true; - |+ console.log("Hello, World!"); - |+ // Modified comment - |+ let x = 5; - |+ return x > 0; - | } - """.trimMargin() + function example() { + - console.log("Hello"); + - // Some comment + - return true; + + console.log("Hello, World!"); + + // Modified comment + + let x = 5; + + return x > 0; + } + """.trimIndent() assertEquals( expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n")