From 02191c25874522c066dbd9e9e88af83a9c24681c Mon Sep 17 00:00:00 2001 From: Andrew Charneski Date: Sun, 31 Mar 2024 20:55:32 -0400 Subject: [PATCH] 1.0.59 (#64) * 1.0.59 * Update UsageManager.kt * wip * Update DiffUtil.kt * wip * wip --- core/build.gradle.kts | 2 +- .../core/platform/file/UsageManager.kt | 8 +- gradle.properties | 2 +- webui/build.gradle.kts | 2 +- .../{SimpleDiffUtil.kt => ApxPatchUtil.kt} | 270 ++++++++++++------ .../simiacryptus/aicoder/util/DiffUtil.kt | 224 ++++++--------- .../skyenet/apps/coding/CodingAgent.kt | 13 +- webui/src/main/resources/application/main.js | 20 +- .../simiacryptus/aicoder/util/DiffUtilTest.kt | 118 ++++++-- 9 files changed, 385 insertions(+), 274 deletions(-) rename webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/{SimpleDiffUtil.kt => ApxPatchUtil.kt} (58%) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e2c9b328..101e4712 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -32,7 +32,7 @@ val jackson_version = "2.15.3" dependencies { - implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.49") + implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.50") implementation("org.apache.commons:commons-text:1.11.0") diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt index e5821648..6e6ac531 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt @@ -58,10 +58,10 @@ open class UsageManager(val root: File) : UsageInterface { ) "cost" -> incrementUsage( - Session(sessionId), - User(email = user), - modelEnum, - com.simiacryptus.jopenai.ApiModel.Usage(cost = value.toDouble()) + session = Session(sessionId = sessionId), + user = User(email = user), + model = modelEnum, + tokens = com.simiacryptus.jopenai.ApiModel.Usage(cost = value.toDouble()) ) else -> throw RuntimeException("Unknown direction $direction") diff --git a/gradle.properties b/gradle.properties index 3c1ad70f..3c6d3091 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # Gradle Releases -> https://github.com/gradle/gradle/releases libraryGroup = com.simiacryptus.skyenet -libraryVersion = 1.0.58 +libraryVersion = 1.0.59 gradleVersion = 7.6.1 diff --git a/webui/build.gradle.kts b/webui/build.gradle.kts index 2f9b919f..3e218d6f 100644 --- a/webui/build.gradle.kts +++ b/webui/build.gradle.kts @@ -35,7 +35,7 @@ val jetty_version = "11.0.18" val jackson_version = "2.15.3" dependencies { - implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.49") + implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.50") implementation(project(":core")) implementation(project(":kotlin")) diff --git a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/SimpleDiffUtil.kt b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt similarity index 58% rename from webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/SimpleDiffUtil.kt rename to webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt index 4945fb48..3a698fcc 100644 --- a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/SimpleDiffUtil.kt +++ b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/ApxPatchUtil.kt @@ -9,12 +9,11 @@ import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.apache.commons.text.similarity.LevenshteinDistance import org.slf4j.LoggerFactory import java.io.File -import java.net.URLDecoder import java.nio.file.Path import java.util.concurrent.TimeUnit import kotlin.io.path.readText -object SimpleDiffUtil { +object ApxPatchUtil { fun patch(source: String, patch: String): String { @@ -151,7 +150,7 @@ fun SocketManagerBase.addApplyDiffLinks( if (fullPatch.contains(diffVal)) return@hrefLink fullPatch.add(diffVal) val newCode = fullPatch.fold(code) { lines, patch -> - SimpleDiffUtil.patch(lines, patch) + ApxPatchUtil.patch(lines, patch) } handle(newCode) task.complete("""
Diff Applied
""") @@ -165,7 +164,7 @@ fun SocketManagerBase.addApplyDiffLinks( fullPatch.add(diffVal) val reversedCode = code.lines().reversed().joinToString("\n") val reversedDiff = diffVal.lines().reversed().joinToString("\n") - val newReversedCode = SimpleDiffUtil.patch(reversedCode, reversedDiff) + val newReversedCode = ApxPatchUtil.patch(reversedCode, reversedDiff) val newCode = newReversedCode.lines().reversed().joinToString("\n") handle(newCode) task.complete("""
Diff Applied (Bottom to Top)
""") @@ -184,11 +183,10 @@ fun SocketManagerBase.addSaveLinks( handle: (String, String) -> Unit ): String { val diffPattern = - """(?s)(? val filename = diffBlock.groupValues[1] -// val language = diffBlock.groupValues[2] val codeValue = diffBlock.groupValues[2] val hrefLink = hrefLink("Save File") { try { @@ -203,7 +201,7 @@ fun SocketManagerBase.addSaveLinks( return withLinks } -private val log = LoggerFactory.getLogger(SimpleDiffUtil::class.java) +private val log = LoggerFactory.getLogger(ApxPatchUtil::class.java) fun findFile(root: Path, filename: String): Path? { return try { @@ -237,106 +235,190 @@ fun SocketManagerBase.addApplyDiffLinks2( task: SessionTask, ui: ApplicationInterface, ): String { - val diffPattern = """(?s)(? - val filename = diffBlock.groupValues[1] - val filepath = try { - findFile(root, filename) ?: root.resolve(filename) - } catch (e: Throwable) { - log.error("Error finding file: $filename", e) - root.resolve(filename) + val headerPattern = """(?s)(?> = diffPattern.findAll(response).map { it.range to it.groupValues[1] }.toList() + val codeblocks = codeblockPattern.findAll(response).filter { + when (it.groupValues[1]) { + "diff" -> false + else -> true } - val diffVal = diffBlock.groupValues[2] - - val prevCode = try { - if (!filepath.toFile().exists()) { - log.warn( - """ - |File not found: $filepath - |Root: ${root.toAbsolutePath()} - |Files: - |${code.keys.joinToString("\n") { "* $it" }} - """.trimMargin() + }.map { it.range to it }.toList() + val withPatchLinks: String = diffs.fold(response) { markdown, diffBlock -> + val header = headers.lastOrNull { it.first.endInclusive < diffBlock.first.start } + val filename = header?.second ?: "Unknown" + val diffVal = diffBlock.second + val newValue = renderDiffBlock(root, filename, code, diffVal, handle, task, ui) + markdown.replace(diffVal, newValue) + } + val withSaveLinks = codeblocks.fold(withPatchLinks) { markdown, codeBlock -> + val header = headers.lastOrNull { it.first.endInclusive < codeBlock.first.start } + val filename = header?.second ?: "Unknown" + val filepath = path(root, filename) + val prevCode = load(filepath, root, code) + val codeLang = codeBlock.second.groupValues[1] + val codeValue = codeBlock.second.groupValues[2] + val hrefLink = hrefLink("Save File") { + try { + handle( + mapOf( + filename to codeValue + ) ) - "" - } else { - filepath.readText(Charsets.UTF_8) + task.complete("""
Saved ${filename}
""") + } catch (e: Throwable) { + task.error(null, e) } - } catch (e: Throwable) { - log.error("Error reading file: $filepath", e) - "" } - val newCode = SimpleDiffUtil.patch(prevCode, diffVal) - val echoDiff = try { - DiffUtil.formatDiff( - DiffUtil.generateDiff( - prevCode.lines(), - newCode.lines() + val codeblockRaw = """ + ```${codeLang} + ${codeValue} + ``` + """.trimIndent() + markdown.replace( + codeblockRaw, displayMapInTabs( + mapOf( + "New" to renderMarkdown(codeblockRaw), + "Old" to renderMarkdown(""" + |```${codeLang} + |${prevCode} + |``` + """.trimMargin()), + "Patch" to renderMarkdown(""" + |```diff + |${ + DiffUtil.formatDiff( + DiffUtil.generateDiff( + prevCode.lines(), + codeValue.lines() + ) + ) + } + |``` + """.trimMargin()), ) + ) + "\n" + hrefLink + ) + } + return withSaveLinks +} + +private fun SocketManagerBase.renderDiffBlock( + root: Path, + filename: String, + code: Map, + diffVal: String, + handle: (Map) -> Unit, + task: SessionTask, + ui: ApplicationInterface +): String { + val filepath = path(root, filename) + val prevCode = load(filepath, root, code) + val newCode = ApxPatchUtil.patch(prevCode, diffVal) + val echoDiff = try { + DiffUtil.formatDiff( + DiffUtil.generateDiff( + prevCode.lines(), + newCode.lines() ) - } catch (e: Throwable) { - renderMarkdown("```\n${e.stackTraceToString()}\n```") - } + ) + } catch (e: Throwable) { + renderMarkdown("```\n${e.stackTraceToString()}\n```") + } - val hrefLink = hrefLink("Apply Diff") { - try { - handle(mapOf(root.relativize(filepath).toString() to SimpleDiffUtil.patch( - prevCode, - diffVal - ) - )) - task.complete("""
Diff Applied
""") + val hrefLink = hrefLink("Apply Diff") { + try { + val relativize = try { + root.relativize(filepath) } catch (e: Throwable) { - task.error(null, e) + filepath } + handle( + mapOf( + relativize.toString() to ApxPatchUtil.patch( + prevCode, + diffVal + ) + ) + ) + task.complete("""
Diff Applied
""") + } catch (e: Throwable) { + task.error(null, e) } - val reverseHrefLink = hrefLink("(Bottom to Top)") { - try { - val reversedCodeMap = code.mapValues { (_, v) -> v.lines().reversed().joinToString("\n") } - val reversedDiff = diffVal.lines().reversed().joinToString("\n") - val newReversedCodeMap = reversedCodeMap.mapValues { (file, prevCode) -> - if (filename == file) { - SimpleDiffUtil.patch(prevCode, reversedDiff).lines().reversed().joinToString("\n") - } else prevCode - } - handle(newReversedCodeMap) - task.complete("""
Diff Applied (Bottom to Top)
""") - } catch (e: Throwable) { - task.error(null, e) + } + val reverseHrefLink = hrefLink("(Bottom to Top)") { + try { + val reversedCodeMap = code.mapValues { (_, v) -> v.lines().reversed().joinToString("\n") } + val reversedDiff = diffVal.lines().reversed().joinToString("\n") + val newReversedCodeMap = reversedCodeMap.mapValues { (file, prevCode) -> + if (filename == file) { + ApxPatchUtil.patch(prevCode, reversedDiff).lines().reversed().joinToString("\n") + } else prevCode } + handle(newReversedCodeMap) + task.complete("""
Diff Applied (Bottom to Top)
""") + } catch (e: Throwable) { + task.error(null, e) } - val diffTask = ui?.newTask() - val prevCodeTask = ui?.newTask() - val newCodeTask = ui?.newTask() - val patchTask = ui?.newTask() - val inTabs = displayMapInTabs( - mapOf( - "Diff" to (diffTask?.placeholder ?: ""), - "Code" to (prevCodeTask?.placeholder ?: ""), - "Preview" to (newCodeTask?.placeholder ?: ""), - "Echo" to (patchTask?.placeholder ?: ""), - ) + } + val diffTask = ui?.newTask() + val prevCodeTask = ui?.newTask() + val newCodeTask = ui?.newTask() + val patchTask = ui?.newTask() + val inTabs = displayMapInTabs( + mapOf( + "Diff" to (diffTask?.placeholder ?: ""), + "Code" to (prevCodeTask?.placeholder ?: ""), + "Preview" to (newCodeTask?.placeholder ?: ""), + "Echo" to (patchTask?.placeholder ?: ""), ) - SocketManagerBase.scheduledThreadPoolExecutor.schedule({ - diffTask?.add(renderMarkdown(/*escapeHtml4*/(diffVal))) - newCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${newCode}\n```")) - prevCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${prevCode}\n```")) - patchTask?.add( - renderMarkdown( - "# $filename\n\n```diff\n ${ - echoDiff?.let { /*escapeHtml4*/URLDecoder.decode( - it, - Charsets.UTF_8 - )/*.indent(" ")*/ - } - }\n```" - ) - ) - }, 100, TimeUnit.MILLISECONDS) - markdown.replace( - diffVal, inTabs + "\n" + hrefLink + "\n" + reverseHrefLink + ) + SocketManagerBase.scheduledThreadPoolExecutor.schedule({ + diffTask?.add(renderMarkdown(/*escapeHtml4*/(diffVal))) + newCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${newCode}\n```")) + prevCodeTask?.add(renderMarkdown("# $filename\n\n```${filename.split('.').lastOrNull() ?: ""}\n${prevCode}\n```")) + patchTask?.add(renderMarkdown("# $filename\n\n```diff\n ${echoDiff}\n```")) + }, 100, TimeUnit.MILLISECONDS) + val newValue = inTabs + "\n" + hrefLink + "\n" + reverseHrefLink + return newValue +} + +private fun load( + filepath: Path?, + root: Path, + code: Map +) = try { + if (true != filepath?.toFile()?.exists()) { + log.warn( + """ + |File not found: $filepath + |Root: ${root.toAbsolutePath()} + |Files: + |${code.keys.joinToString("\n") { "* $it" }} + """.trimMargin() ) + "" + } else { + filepath.readText(Charsets.UTF_8) } - return withLinks +} catch (e: Throwable) { + log.error("Error reading file: $filepath", e) + "" +} + +private fun path(root: Path, filename: String): Path? { + val filepath = try { + findFile(root, filename) ?: root.resolve(filename) + } catch (e: Throwable) { + log.error("Error finding file: $filename", e) + try { + root.resolve(filename) + } catch (e: Throwable) { + log.error("Error resolving file: $filename", e) + File(filename).toPath() + } + } + return filepath } \ No newline at end of file diff --git a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/DiffUtil.kt b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/DiffUtil.kt index 781e0c0e..5bc8daa3 100644 --- a/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/DiffUtil.kt +++ b/webui/src/main/kotlin/com/github/simiacryptus/aicoder/util/DiffUtil.kt @@ -1,11 +1,29 @@ package com.github.simiacryptus.aicoder.util -import kotlin.math.max +import com.github.simiacryptus.aicoder.util.PatchLineType.* -sealed class DiffResult { - data class Added(val line: String) : DiffResult() - data class Deleted(val line: String) : DiffResult() - data class Unchanged(val line: String) : DiffResult() +enum class PatchLineType { + Added, Deleted, Unchanged +} + +data class PatchLine( + val type: PatchLineType, + val lineNumber: Int, + val line: String, + val compareText: String = line.trim(), +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PatchLine + + return compareText == other.compareText + } + + override fun hashCode(): Int { + return compareText.hashCode() + } } object DiffUtil { @@ -18,75 +36,57 @@ object DiffUtil { * @param modified The modified list of strings. * @return A list of DiffResult indicating the differences. */ - fun generateDiff(original: List, modified: List): List { - val diffResults = mutableListOf() - - if (original == modified) return diffResults - // Fix: Initialize pointers for both lists - var oIndex = 0 - var mIndex = 0 - // This simple implementation is for demonstration purposes - original.forEachIndexed { index, line -> - // If the current index is beyond the size of the modified list, the line is considered deleted - when { - // If the line at the current index is the same in both original and modified, it's unchanged - // If the line at the current index is different, the original line is considered deleted - index == 0 && original[index] != modified.getOrNull(index) -> { - diffResults.add(DiffResult.Deleted(original[index])) - diffResults.add(DiffResult.Added(modified[index])) - } // Fix: Remove this block as it incorrectly handles the first line special case - - index >= modified.size -> { - diffResults.add(DiffResult.Deleted(line)) - } - - modified.getOrNull(index) == line -> { - diffResults.add(DiffResult.Unchanged(line)) - } - - else -> { - if (index < modified.size && original[index] != modified[index]) { - // Check if the line is truly deleted or just modified - if (!modified.contains(original[index])) { - diffResults.add(DiffResult.Deleted(original[index])) - } - if (!original.contains(modified[index])) { - diffResults.add(DiffResult.Added(modified[index])) + fun generateDiff(original: List, modified: List): List { + if (original == modified) return modified.withIndex().map { (i, v) -> PatchLine(Unchanged, i, v) } + val original = original.withIndex().map { (i, v) -> PatchLine(Unchanged, i, v) }.toMutableList() + val modified = modified.withIndex().map { (i, v) -> PatchLine(Unchanged, i, v) }.toMutableList() + val patchLines = mutableListOf() + + while (original.isNotEmpty() && modified.isNotEmpty()) { + val originalLine = original.first() + val modifiedLine = modified.first() + + if (originalLine == modifiedLine) { + patchLines.add(PatchLine(Unchanged, originalLine.lineNumber, originalLine.line)) + original.removeAt(0) + modified.removeAt(0) + } else { + val originalIndex = original.indexOf(modifiedLine).let { if (it == -1) null else it } + val modifiedIndex = modified.indexOf(originalLine).let { if (it == -1) null else it } + + if (originalIndex != null && modifiedIndex != null) { + if (originalIndex < modifiedIndex) { + while(original.first() != modifiedLine) { + patchLines.add(PatchLine(Deleted, original.first().lineNumber, original.first().line)) + original.removeAt(0) } - oIndex++ // Fix: Increment original index to correctly track progress - } else if (modified.subList(index, modified.size).contains(line)) { - diffResults.add(DiffResult.Added(modified[index])) } else { - diffResults.add(DiffResult.Deleted(line)) + while(modified.first() != originalLine) { + patchLines.add(PatchLine(Added, modified.first().lineNumber, modified.first().line)) + modified.removeAt(0) + } } - } - } - - // Handle additional lines in the modified text - if (index == original.lastIndex && modified.size > original.size) { - // For each line in modified that doesn't have a corresponding line in original, mark it as added - if (index >= original.size) { - modified.subList(max(original.size, index), modified.size).forEach { - diffResults.add(DiffResult.Added(it)) + } else if (originalIndex != null) { + while(original.first() != modifiedLine) { + patchLines.add(PatchLine(Deleted, original.first().lineNumber, original.first().line)) + original.removeAt(0) + } + } else if (modifiedIndex != null) { + while(modified.first() != originalLine) { + patchLines.add(PatchLine(Added, modified.first().lineNumber, modified.first().line)) + modified.removeAt(0) } - mIndex = modified.size // Fix: Ensure modified index is updated to prevent over-processing + } else { + patchLines.add(PatchLine(Deleted, originalLine.lineNumber, originalLine.line)) + original.removeAt(0) + patchLines.add(PatchLine(Added, modifiedLine.lineNumber, modifiedLine.line)) + modified.removeAt(0) } } } - - return diffResults - } - - /** - * Determines if the provided context buffer is at the end of the diff. - * This function checks if all elements in the context buffer are of type Unchanged, indicating no more changes ahead. - * - * @param contextBuffer The list of DiffResult to check. - * @return True if all elements are Unchanged, false otherwise. - */ - fun isEndOfDiff(contextBuffer: List): Boolean { - // Check if all elements in the context buffer are unchanged, indicating the end of the diff - return contextBuffer.all { it is DiffResult.Unchanged } + patchLines.addAll(original.map { PatchLine(Deleted, it.lineNumber, it.line) }) + patchLines.addAll(modified.map { PatchLine(Added, it.lineNumber, it.line) }) + return patchLines } /** @@ -94,80 +94,36 @@ object DiffUtil { * This function processes each diff result to format added, deleted, and unchanged lines appropriately, * including context lines and markers for easier reading. * - * @param diffResults The list of DiffResult to format. + * @param patchLines The list of DiffResult to format. * @param contextLines The number of context lines to include around changes. * @return A formatted string representing the diff. */ - fun formatDiff(diffResults: List, contextLines: Int = 3): String { - val formattedDiff = StringBuilder() - var contextBuffer = mutableListOf() - var linesSinceChange = 0 - var inContext = false - - fun flushContextBuffer() { - if (contextBuffer.isNotEmpty()) { - // Determine if the context buffer is at the end of the diff results to decide on adding the context marker - // Only add context marker if we are not at the start of the diff - // Check if the context buffer is at the end of the diff results to decide on adding the context marker - val isAtEndOfDiff = diffResults.indexOf(contextBuffer.last()) == diffResults.size - 1 - val shouldAddContextMarker = - contextLines > 0 && formattedDiff.isNotEmpty() && !isEndOfDiff(contextBuffer) && !isAtEndOfDiff && linesSinceChange >= contextLines - // Add context marker if not at the start, and there are context lines to show - if (shouldAddContextMarker) { - formattedDiff.append("...\n") // Add context marker if not at the start and contextLines > 0 + fun formatDiff(patchLines: List, contextLines: Int = 3): String { + val patchList = patchLines.withIndex().filter { (idx, lineDiff) -> + when (lineDiff.type) { + Added -> true + Deleted -> true + Unchanged -> { + val distBackwards = + patchLines.subList(0, idx).indexOfLast { it.type != Unchanged }.let { if (it == -1) null else idx - it } + val distForwards = patchLines.subList(idx, patchLines.size).indexOfFirst { it.type != Unchanged } + .let { if (it == -1) null else it } + (null != distBackwards && distBackwards <= contextLines) || (null != distForwards && distForwards <= contextLines) } - contextBuffer.forEach { result -> - when (result) { - // Format added lines with a "+" prefix - is DiffResult.Added -> formattedDiff.append("+${result.line}\n") - // Format deleted lines with a "-" prefix - is DiffResult.Deleted -> formattedDiff.append("-${result.line}\n") - // Format unchanged lines with a space prefix, if context lines are to be included - is DiffResult.Unchanged -> if (contextLines > 0) formattedDiff.append(" ${result.line}\n") - } - } - contextBuffer.clear() - linesSinceChange = 0 } - } + }.map { it.value }.toTypedArray() - - for (result in diffResults) { - when (result) { - // Handle unchanged lines, considering context lines and whether we're at the end of the diff - is DiffResult.Unchanged -> { - if (linesSinceChange < contextLines || inContext) { // Fix: Ensure context is correctly managed when in context - flushContextBuffer() - inContext = contextLines > 0 - } - if (linesSinceChange < contextLines || diffResults.indexOf(result) == diffResults.size - 1) { - contextBuffer.add(result) - linesSinceChange++ - if (contextBuffer.size > contextLines) contextBuffer.removeAt(0) - } else if (contextLines > 0) { - formattedDiff.append(" ${result.line}\n") - } - } - - // For added or deleted lines, reset context and prepare to flush if needed - else -> { - inContext = false // Fix: Correctly reset inContext flag to manage context flushing - flushContextBuffer() - contextBuffer.add(result) - } - } - // Flush the context buffer if we've reached the maximum number of context lines, or if we're in a context section - if (linesSinceChange >= contextLines && contextLines > 0) { - flushContextBuffer() - inContext = contextLines > 0 + return patchList.withIndex().joinToString("\n") { (idx, lineDiff) -> + when { + idx == 0 -> "" + lineDiff.type != Unchanged || patchList[idx - 1].type != Unchanged -> "" + patchList[idx - 1].lineNumber + 1 < lineDiff.lineNumber -> "...\n" + else -> "" + } + when (lineDiff.type) { + Added -> "+ ${lineDiff.line}" + Deleted -> "- ${lineDiff.line}" + Unchanged -> " ${lineDiff.line}" } - // Ensure the context buffer does not exceed the specified number of context lines - if (contextBuffer.size > contextLines) contextBuffer.removeAt(0) - // Reset linesSinceChange counter when in context to accurately track distance from last change - if (inContext) linesSinceChange = 0 } - - flushContextBuffer() // Flush remaining changes - return formattedDiff.toString().trimEnd('\n') } } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt index 948361bd..9ea16216 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt @@ -65,14 +65,17 @@ open class CodingAgent( try { mainTask.echo(renderMarkdown(userMessage)) val codeRequest = codeRequest(listOf(userMessage to ApiModel.Role.user)) - newRetryable(mainTask, codeRequest) + start(codeRequest, mainTask) } catch (e: Throwable) { log.warn("Error", e) mainTask.error(ui, e) } } - private fun newRetryable(task: SessionTask, codeRequest: CodingActor.CodeRequest) { + fun start( + codeRequest: CodingActor.CodeRequest, + task: SessionTask = mainTask, + ) { val newTask = ui.newTask() task.complete(newTask.placeholder) Retryable(ui, newTask) { @@ -91,7 +94,7 @@ open class CodingAgent( } } - protected open fun codeRequest(messages: List>) = + open fun codeRequest(messages: List>) = CodingActor.CodeRequest(messages) fun displayCode( @@ -244,13 +247,13 @@ open class CodingAgent( ) { try { task.echo(renderMarkdown(feedback)) - newRetryable(task, codeRequest( + 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) diff --git a/webui/src/main/resources/application/main.js b/webui/src/main/resources/application/main.js index 34514865..2357e416 100644 --- a/webui/src/main/resources/application/main.js +++ b/webui/src/main/resources/application/main.js @@ -96,15 +96,17 @@ let loadImages = "true"; if (this.isPanning === false) return; const dx = event.clientX - this.startX; const dy = event.clientY - this.startY; - if (this.currentTransform.x) { - this.currentTransform.x = dx * moveScale + this.priorPan.x; - } else { - this.currentTransform.x = dx * moveScale + this.priorPan.x; - } - if (this.currentTransform.y) { - this.currentTransform.y = dy * moveScale + this.priorPan.y; - } else { - this.currentTransform.y = dy * moveScale + this.priorPan.y; + if(this.priorPan) { + if (this.currentTransform.x) { + this.currentTransform.x = dx * moveScale + this.priorPan.x; + } else { + this.currentTransform.x = dx * moveScale + this.priorPan.x; + } + if (this.currentTransform.y) { + this.currentTransform.y = dy * moveScale + this.priorPan.y; + } else { + this.currentTransform.y = dy * moveScale + this.priorPan.y; + } } console.log("Panning %s, %s", this.currentTransform.x, this.currentTransform.y); this.updateTransform(); diff --git a/webui/src/test/kotlin/com/github/simiacryptus/aicoder/util/DiffUtilTest.kt b/webui/src/test/kotlin/com/github/simiacryptus/aicoder/util/DiffUtilTest.kt index 3260d1fb..048052ec 100644 --- a/webui/src/test/kotlin/com/github/simiacryptus/aicoder/util/DiffUtilTest.kt +++ b/webui/src/test/kotlin/com/github/simiacryptus/aicoder/util/DiffUtilTest.kt @@ -1,11 +1,11 @@ package com.github.simiacryptus.aicoder.util -import com.github.simiacryptus.aicoder.util.DiffUtil import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test class DiffUtilTest { +/* @Test fun testNoChanges() { val original = listOf("line1", "line2", "line3") @@ -14,6 +14,7 @@ class DiffUtilTest { val formattedDiff = DiffUtil.formatDiff(diffResults) assertEquals("", formattedDiff, "There should be no diff for identical inputs.") } +*/ // @Test fun testAdditions() { @@ -22,9 +23,9 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults) val expectedDiff = """ - line1 - line2 - +line3 + line1 + line2 + + line3 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly represent an addition.") } @@ -36,9 +37,9 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults) val expectedDiff = """ - line1 - -line2 - line3 + line1 + - line2 + line3 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly represent a deletion.") } @@ -50,10 +51,10 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults) val expectedDiff = """ - line1 - -line2 - +line3 - line4 + line1 + - line2 + + line3 + line4 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly represent mixed changes.") } @@ -65,10 +66,10 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults, 1) val expectedDiff = """ - line1 - -line2 - +changed_line2 - line3 + line1 + - line2 + + changed_line2 + line3 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly include context lines.") } @@ -80,9 +81,9 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults) val expectedDiff = """ - -line1 - +changed_line1 - line2 + - line1 + + changed_line1 + line2 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly represent changes at the start.") } @@ -94,9 +95,9 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults) val expectedDiff = """ - line1 - -line2 - +changed_line2 + line1 + - line2 + + changed_line2 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly represent changes at the end.") } @@ -108,11 +109,78 @@ class DiffUtilTest { val diffResults = DiffUtil.generateDiff(original, modified) val formattedDiff = DiffUtil.formatDiff(diffResults, 0) val expectedDiff = """ - line1 - -line2 - +changed_line2 - line3 + line1 + - line2 + + changed_line2 + line3 """.trimIndent() assertEquals(expectedDiff, formattedDiff, "The diff should correctly handle cases with no context lines.") } + + @Test + fun testVerifyLLMPatch() { + val originalCode = """ + + + + + + Speed Word Search + + + +
+

Speed Word Search

+
+

Score: 0

+

Time Left: 60 seconds

+
+
+ +
+ + +
+ + + + """.trimIndent() + val llmPatch = """ + + + + + + Speed Word Search + + + +
+

Speed Word Search

+ +
+ +

Find as many words as possible in 60 seconds. Start typing to begin!

+ +
+
+

Score: 0

+

Time Left: 60 seconds

+
+
+ +
+ + +
+ + + + """.trimIndent() + val reconstructed = ApxPatchUtil.patch(originalCode, llmPatch) + + val patchLines = DiffUtil.generateDiff(originalCode.lines(), reconstructed.lines()) +// println("\n\nPatched:\n\n") +// patchLines.forEach { println(it) } + + println("\n\nEcho Patch:\n\n") + DiffUtil.formatDiff(patchLines).lines().forEach { println(it) } + } }