From 9327da6d24e71d4ea16f578b0466f8175b02d0e6 Mon Sep 17 00:00:00 2001 From: Andrew Charneski Date: Mon, 25 Mar 2024 02:46:24 -0400 Subject: [PATCH] 1.3.4 (#140) * 1.3.4 * 1.3.4 * 1.3.4 * wip * Update TaskRunnerAction.kt * Update DiffChatAction.kt --- build.gradle.kts | 2 +- gradle.properties | 2 +- .../aicoder/actions/code/CommentsAction.kt | 11 +- .../aicoder/actions/code/CustomEditAction.kt | 9 +- .../aicoder/actions/code/DescribeAction.kt | 11 +- .../aicoder/actions/code/DocAction.kt | 11 +- .../actions/code/ImplementStubAction.kt | 11 +- .../code/InsertImplementationAction.kt | 11 +- .../aicoder/actions/code/PasteAction.kt | 3 +- .../actions/code/RenameVariablesAction.kt | 11 +- .../actions/dev/InternalCoderAction.kt | 3 +- .../actions/generic/AnalogueFileAction.kt | 3 +- .../aicoder/actions/generic/AppendAction.kt | 7 +- .../aicoder/actions/generic/AutoDevAction.kt | 122 ++++-- .../aicoder/actions/generic/CodeChatAction.kt | 3 +- .../actions/generic/CreateFileAction.kt | 7 +- .../aicoder/actions/generic/DiffChatAction.kt | 36 +- .../generic/DocumentationCompilerAction.kt | 104 ++++- .../actions/generic/LineFilterChatAction.kt | 6 +- .../actions/generic/MultiDiffChatAction.kt | 35 +- .../actions/generic/ReplaceOptionsAction.kt | 11 +- .../actions/generic/TaskRunnerAction.kt | 398 +++++++++++------- .../aicoder/actions/generic/WebDevAction.kt | 58 ++- .../markdown/MarkdownImplementActionGroup.kt | 11 +- .../actions/markdown/MarkdownListAction.kt | 9 +- .../aicoder/config/ActionSettingsRegistry.kt | 13 +- .../aicoder/config/AppSettingsComponent.kt | 17 +- .../aicoder/config/AppSettingsState.kt | 14 +- .../config/StaticAppSettingsConfigurable.kt | 16 +- .../aicoder/ui/ModelSelectionWidgetFactory.kt | 152 +++---- .../simiacryptus/aicoder/util/UITools.kt | 119 ++---- .../aicoder/actions/ActionTestBase.kt | 4 +- 32 files changed, 738 insertions(+), 492 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e93e34e5..07aaa7be 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,7 +26,7 @@ repositories { val kotlin_version = "1.9.21" val jetty_version = "11.0.18" val slf4j_version = "2.0.9" -val skyenet_version = "1.0.53" +val skyenet_version = "1.0.55" val remoterobot_version = "0.11.21" dependencies { diff --git a/gradle.properties b/gradle.properties index 1faec893..53fa6eb9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ pluginName=intellij-aicoder pluginRepositoryUrl=https://github.com/SimiaCryptus/intellij-aicoder -pluginVersion=1.3.3 +pluginVersion=1.3.4 jvmArgs=-Xmx8g org.gradle.jvmargs=-Xmx8g diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt index 1793a0a2..a9113fe2 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.intellij.openapi.project.Project import com.simiacryptus.jopenai.proxy.ChatProxy @@ -18,11 +19,11 @@ class CommentsAction : SelectionAction() { override fun processSelection(state: SelectionState, config: String?): String { return ChatProxy( - clazz = CommentsAction_VirtualAPI::class.java, - api = api, - temperature = AppSettingsState.instance.temperature, - model = AppSettingsState.instance.defaultChatModel(), - deserializerRetries = 5 + clazz = CommentsAction_VirtualAPI::class.java, + api = api, + temperature = AppSettingsState.instance.temperature, + model = AppSettingsState.instance.smartModel.chatModel(), + deserializerRetries = 5 ).create().editCode( state.selectedText ?: "", "Add comments to each line explaining the code", diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt index 53010d4b..2a97bbd2 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.project.Project import com.simiacryptus.jopenai.proxy.ChatProxy @@ -25,10 +26,10 @@ open class CustomEditAction : SelectionAction() { val proxy: VirtualAPI get() { val chatProxy = ChatProxy( - clazz = VirtualAPI::class.java, - api = api, - temperature = AppSettingsState.instance.temperature, - model = AppSettingsState.instance.defaultChatModel(), + clazz = VirtualAPI::class.java, + api = api, + temperature = AppSettingsState.instance.temperature, + model = AppSettingsState.instance.smartModel.chatModel(), ) chatProxy.addExample( VirtualAPI.EditedText( diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt index 65e8ce1b..9eddc07c 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.IndentedText import com.intellij.openapi.project.Project import com.simiacryptus.jopenai.proxy.ChatProxy @@ -24,11 +25,11 @@ class DescribeAction : SelectionAction() { private val proxy: DescribeAction_VirtualAPI get() = ChatProxy( - clazz = DescribeAction_VirtualAPI::class.java, - api = api, - temperature = AppSettingsState.instance.temperature, - model = AppSettingsState.instance.defaultChatModel(), - deserializerRetries = 5 + clazz = DescribeAction_VirtualAPI::class.java, + api = api, + temperature = AppSettingsState.instance.temperature, + model = AppSettingsState.instance.smartModel.chatModel(), + deserializerRetries = 5 ).create() override fun getConfig(project: Project?): String { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt index 568684dc..f8b3ae30 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.IndentedText import com.github.simiacryptus.aicoder.util.psi.PsiUtil @@ -26,11 +27,11 @@ class DocAction : SelectionAction() { private val proxy: DocAction_VirtualAPI by lazy { val chatProxy = ChatProxy( - clazz = DocAction_VirtualAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = DocAction_VirtualAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ) chatProxy.addExample( DocAction_VirtualAPI.DocAction_ConvertedText().apply { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt index 5fca34ce..6278086e 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.psi.PsiUtil import com.intellij.openapi.project.Project @@ -27,11 +28,11 @@ class ImplementStubAction : SelectionAction() { private fun getProxy(): VirtualAPI { return ChatProxy( - clazz = VirtualAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = VirtualAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ).create() } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt index 8dec68de..ea99154c 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.TextBlock import com.github.simiacryptus.aicoder.util.UITools @@ -29,11 +30,11 @@ class InsertImplementationAction : SelectionAction() { private fun getProxy(): VirtualAPI { return ChatProxy( - clazz = VirtualAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = VirtualAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ).create() } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt index 251a6c93..81739c01 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project @@ -28,7 +29,7 @@ open class PasteAction : SelectionAction(false) { return ChatProxy( VirtualAPI::class.java, api, - AppSettingsState.instance.defaultChatModel(), + AppSettingsState.instance.smartModel.chatModel(), AppSettingsState.instance.temperature, ).create().convert( getClipboard().toString().trim(), diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt index db2f638d..69cf001f 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.actionSystem.AnActionEvent @@ -29,11 +30,11 @@ open class RenameVariablesAction : SelectionAction() { val proxy: RenameAPI get() { return ChatProxy( - clazz = RenameAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = RenameAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ).create() } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/InternalCoderAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/InternalCoderAction.kt index d0ae6bc9..977e10b6 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/InternalCoderAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/InternalCoderAction.kt @@ -3,6 +3,7 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.IdeaKotlinInterpreter import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -52,7 +53,7 @@ class InternalCoderAction : BaseAction() { symbols = symbols, temperature = 0.1, details = "Ensure that responses are printed; this is not a REPL.", - model = AppSettingsState.instance.defaultChatModel(), + model = AppSettingsState.instance.smartModel.chatModel(), ) Thread { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AnalogueFileAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AnalogueFileAction.kt index 1613f0cc..b18cf034 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AnalogueFileAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AnalogueFileAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.FileContextAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.config.Name import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.application.ApplicationManager @@ -89,7 +90,7 @@ class AnalogueFileAction : FileContextAction() { } private fun generateFile(baseFile: ProjectFile, directive: String): ProjectFile { - val model = AppSettingsState.instance.defaultChatModel() + val model = AppSettingsState.instance.smartModel.chatModel() val chatRequest = ApiModel.ChatRequest( model = model.modelName, temperature = AppSettingsState.instance.temperature, diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt index 9475134a..0df10210 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt @@ -2,6 +2,7 @@ import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.intellij.openapi.project.Project import com.simiacryptus.jopenai.ApiModel.* import com.simiacryptus.jopenai.util.ClientUtil.toContentList @@ -14,8 +15,8 @@ import com.simiacryptus.jopenai.util.ClientUtil.toContentList override fun processSelection(state: SelectionState, config: String?): String { val settings = AppSettingsState.instance val request = ChatRequest( - model = settings.defaultChatModel().modelName, - temperature = settings.temperature + model = settings.smartModel.chatModel().modelName, + temperature = settings.temperature ).copy( temperature = settings.temperature, messages = listOf( @@ -23,7 +24,7 @@ import com.simiacryptus.jopenai.util.ClientUtil.toContentList ChatMessage(Role.user, state.selectedText.toString().toContentList(), null) ), ) - val chatResponse = api.chat(request, settings.defaultChatModel()) + val chatResponse = api.chat(request, settings.smartModel.chatModel()) val b4 = state.selectedText ?: "" val str = chatResponse.choices[0].message?.content ?: "" return b4 + if (str.startsWith(b4)) str.substring(b4.length) else str diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AutoDevAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AutoDevAction.kt index 8410a1c1..07897e54 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AutoDevAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AutoDevAction.kt @@ -3,20 +3,23 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer +import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.util.UITools -import com.github.simiacryptus.aicoder.util.addApplyDiffLinks +import com.github.simiacryptus.aicoder.util.addApplyDiffLinks2 import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.PlatformDataKeys import com.simiacryptus.jopenai.API +import com.simiacryptus.jopenai.ApiModel +import com.simiacryptus.jopenai.ApiModel.Role import com.simiacryptus.jopenai.describe.Description import com.simiacryptus.jopenai.models.ChatModels import com.simiacryptus.jopenai.proxy.ValidatedObject -import com.simiacryptus.jopenai.util.JsonUtil +import com.simiacryptus.jopenai.util.ClientUtil.toContentList +import com.simiacryptus.jopenai.util.JsonUtil.toJson +import com.simiacryptus.skyenet.Acceptable import com.simiacryptus.skyenet.AgentPatterns -import com.simiacryptus.skyenet.core.actors.ActorSystem -import com.simiacryptus.skyenet.core.actors.BaseActor -import com.simiacryptus.skyenet.core.actors.ParsedActor -import com.simiacryptus.skyenet.core.actors.SimpleActor +import com.simiacryptus.skyenet.Retryable +import com.simiacryptus.skyenet.core.actors.* import com.simiacryptus.skyenet.core.platform.* import com.simiacryptus.skyenet.core.platform.file.DataStorage import com.simiacryptus.skyenet.webui.application.ApplicationInterface @@ -26,6 +29,8 @@ import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory import java.awt.Desktop import java.io.File +import java.util.concurrent.Semaphore +import java.util.concurrent.atomic.AtomicReference class AutoDevAction : BaseAction() { @@ -77,8 +82,9 @@ class AutoDevAction : BaseAction() { user = user, ui = ui, tools = settings.tools, - model = settings.model, + model = settings.model!!, event = event, + parsingModel = AppSettingsState.instance.defaultFastModel(), ).start( userMessage = userMessage, ) @@ -87,7 +93,7 @@ class AutoDevAction : BaseAction() { data class Settings( val budget: Double? = 2.00, val tools: List = emptyList(), - val model: ChatModels = ChatModels.GPT4Turbo, + val model: ChatModels? = AppSettingsState.instance.defaultSmartModel(), ) override val settingsClass: Class<*> get() = Settings::class.java @@ -103,8 +109,9 @@ class AutoDevAction : BaseAction() { user: User?, val ui: ApplicationInterface, val model: ChatModels, + val parsingModel: ChatModels, val tools: List = emptyList(), - private val actorMap: Map> = mapOf( + actorMap: Map> = mapOf( ActorTypes.DesignActor to ParsedActor( resultClass = TaskList::class.java, prompt = """ @@ -113,7 +120,7 @@ class AutoDevAction : BaseAction() { For each task, provide a list of files to be modified and a description of the changes to be made. """.trimIndent(), model = model, - parsingModel = model, + parsingModel = parsingModel, ), ActorTypes.TaskCodingActor to SimpleActor( prompt = """ @@ -140,7 +147,8 @@ class AutoDevAction : BaseAction() { ), ), val event: AnActionEvent, - ) : ActorSystem(actorMap.map { it.key.name to it.value.javaClass }.toMap(), dataStorage, user, session) { + ) : ActorSystem( + actorMap.map { it.key.name to it.value }.toMap(), dataStorage, user, session) { enum class ActorTypes { DesignActor, TaskCodingActor, @@ -153,7 +161,8 @@ class AutoDevAction : BaseAction() { userMessage: String, ) { val codeFiles = mutableMapOf() - val root = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(event.dataContext)?.map { it.toFile.toPath() }?.toTypedArray()?.commonRoot()!! + val root = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(event.dataContext) + ?.map { it.toFile.toPath() }?.toTypedArray()?.commonRoot()!! PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(event.dataContext)?.forEach { file -> val code = file.inputStream.bufferedReader().use { it.readText() } codeFiles[root.relativize(file.toNioPath()).toString()] = code @@ -165,29 +174,57 @@ class AutoDevAction : BaseAction() { }\n$code\n```" } - val architectureResponse = AgentPatterns.iterate( - input = userMessage, - heading = userMessage, - actor = designActor, - toInput = { listOf(codeSummary(), it) }, - api = api, + val task = ui.newTask() + val toInput = { it: String -> listOf(codeSummary(), it) } + val architectureResponse = Acceptable( + task = task, + userMessage = userMessage, + initialResponse = { it: String -> designActor.answer(toInput(it), api = api) }, + outputFn = { design: ParsedResponse -> + // renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj)}\n```") + AgentPatterns.displayMapInTabs(mapOf( + "Text" to renderMarkdown(design.text), + "JSON" to renderMarkdown("```json\n${toJson(design.obj)}\n```"), + ) + ) + }, ui = ui, - outputFn = { design -> - renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj)}\n```") - } - ) + reviseResponse = { userMessages: List> -> + designActor.respond( + messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }.toTypedArray()), + input = toInput(userMessage), + api = api + ) + }, + atomicRef = AtomicReference(), + semaphore = Semaphore(0), + heading = userMessage + ).call() - val task = ui.newTask() try { architectureResponse.obj.tasks.forEach { (paths, description) -> task.complete(ui.hrefLink(renderMarkdown("Task: $description")){ val task = ui.newTask() task.header("Task: $description") - AgentPatterns.retryable(ui,task) { + val process = { it: StringBuilder -> val filter = codeFiles.filter { (path, _) -> paths?.find { path.contains(it) }?.isNotEmpty() == true } - require(filter.isNotEmpty()) { "No files found for $paths" } + require(filter.isNotEmpty()) { + """ + |No files found for $paths + | + |Root: + |$root + | + |Files: + |${codeFiles.keys.joinToString("\n")} + | + |Paths: + |${paths?.joinToString("\n") ?: ""} + | + """.trimMargin() + } renderMarkdown( - ui.socketManager.addApplyDiffLinks( + ui.socketManager.addApplyDiffLinks2( code = codeFiles, response = taskActor.answer(listOf( codeSummary(), @@ -197,24 +234,27 @@ class AutoDevAction : BaseAction() { }, architectureResponse.text, "Provide a change for ${paths?.joinToString(",") { it } ?: ""} ($description)" - ), api) - ) { newCodeMap -> - newCodeMap.forEach { (path, newCode) -> - val prev = codeFiles[path] - if (prev != newCode) { - codeFiles[path] = newCode - task.complete( - "$path Updated" - ) + ), api), + task = task, + handle = { newCodeMap -> + newCodeMap.forEach { (path, newCode) -> + val prev = codeFiles[path] + if (prev != newCode) { + codeFiles[path] = newCode + task.complete( + "$path Updated" + ) + } } } - }) + )) } + Retryable(ui, task, process).apply { addTab(ui, process(container!!)) } }) } } catch (e: Throwable) { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CodeChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CodeChatAction.kt index 7feaca97..a4e0496c 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CodeChatAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CodeChatAction.kt @@ -4,6 +4,7 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.CodeChatSocketManager import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.intellij.openapi.actionSystem.AnActionEvent @@ -36,7 +37,7 @@ class CodeChatAction : BaseAction() { codeSelection = editor.caretModel.primaryCaret.selectedText ?: editor.document.text, filename = filename, api = api, - model = AppSettingsState.instance.defaultChatModel(), + model = AppSettingsState.instance.smartModel.chatModel(), storage = ApplicationServices.dataStorageFactory(root) ) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CreateFileAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CreateFileAction.kt index 9619df8b..1e95ad49 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CreateFileAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CreateFileAction.kt @@ -2,11 +2,10 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.FileContextAction import com.github.simiacryptus.aicoder.config.AppSettingsState -import com.github.simiacryptus.aicoder.config.Name +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.simiacryptus.jopenai.ApiModel.* import com.simiacryptus.jopenai.util.ClientUtil.toContentList import java.io.File -import javax.swing.JTextArea class CreateFileAction : FileContextAction(false, true) { @@ -49,7 +48,7 @@ class CreateFileAction : FileContextAction(false, tru basePath: String, directive: String ): ProjectFile { - val model = AppSettingsState.instance.defaultChatModel() + val model = AppSettingsState.instance.smartModel.chatModel() val chatRequest = ChatRequest( model = model.modelName, temperature = AppSettingsState.instance.temperature, @@ -74,7 +73,7 @@ class CreateFileAction : FileContextAction(false, tru ) val response = api.chat( chatRequest, - AppSettingsState.instance.defaultChatModel() + AppSettingsState.instance.smartModel.chatModel() ).choices?.first()?.message?.content?.trim() ?: "" var outputPath = basePath val header = response.lines().first() diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DiffChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DiffChatAction.kt index a7eea799..ed1c182d 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DiffChatAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DiffChatAction.kt @@ -4,6 +4,7 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.CodeChatSocketManager import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.addApplyDiffLinks @@ -17,6 +18,7 @@ import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.chat.ChatServer +import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.session.SocketManager import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory @@ -35,8 +37,8 @@ class DiffChatAction : BaseAction() { val filename = FileDocumentManager.getInstance().getFile(document)?.name ?: return val primaryCaret = editor.caretModel.primaryCaret val rawText: String - val selectionStart : Int - val selectionEnd : Int + val selectionStart: Int + val selectionEnd: Int val selectedText = primaryCaret.selectedText if (null != selectedText) { rawText = selectedText @@ -58,21 +60,37 @@ class DiffChatAction : BaseAction() { codeSelection = numberedText, filename = filename, api = api, - model = AppSettingsState.instance.defaultChatModel(), + model = AppSettingsState.instance.smartModel.chatModel(), storage = ApplicationServices.dataStorageFactory(root) ) { override val systemPrompt: String get() = super.systemPrompt + """ - Provide code patches in diff format within ```diff code blocks. - The diff format should use + for line additions, - for line deletions. - The diff should include sufficient context before every change to identify the location. + Please provide code modifications in the following diff format within triple-backtick diff code blocks. Each diff block should be preceded by a header that identifies the file being modified. + + The diff format rules are as follows: + - Use '-' at the beginning of a line to indicate a deletion. + - Use '+' at the beginning of a line to indicate an addition. + - Include 2 lines of context before and after every change to help identify the location of the change. + - If a line is part of the original code and hasn't been modified, simply include it without '+' or '-'. + - Lines starting with "@@" or "---" or "+++" are treated as headers and are ignored. + + Example: + + ### Path/To/YourFile.ext + ```diff + - This line will be removed. + + This line will be added. + ``` + + Note: The diff should accurately reflect the changes to be made to the code, including sufficient context to ensure the modifications can be correctly applied. """.trimIndent() - override fun renderResponse(response: String): String { - val withLinks = addApplyDiffLinks(rawText, response) { newCode -> + + override fun renderResponse(response: String, task: SessionTask): String { + val withLinks = addApplyDiffLinks(rawText, response, handle = { newCode: String -> WriteCommandAction.runWriteCommandAction(e.project) { document.replaceString(selectionStart, selectionEnd, newCode) } - } + }, task = task) val html = renderMarkdown(withLinks) return """
$html
""" } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DocumentationCompilerAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DocumentationCompilerAction.kt index d0f6cf3d..c3a82d2b 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DocumentationCompilerAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DocumentationCompilerAction.kt @@ -2,18 +2,23 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.FileContextAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.config.Name import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.ui.CheckBoxList import com.intellij.ui.components.JBScrollPane +import com.intellij.ui.components.JBTextField import com.simiacryptus.jopenai.ApiModel import com.simiacryptus.jopenai.util.ClientUtil.toContentList import org.apache.commons.io.IOUtils +import java.awt.BorderLayout +import java.awt.Dimension import java.io.File import java.io.FileInputStream import java.nio.file.Files @@ -21,7 +26,11 @@ import java.nio.file.Path import java.util.concurrent.Executors import java.util.concurrent.Future import java.util.concurrent.TimeUnit -import javax.swing.JTextArea +import javax.swing.BoxLayout +import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel + class DocumentationCompilerAction : FileContextAction() { @@ -31,9 +40,14 @@ class DocumentationCompilerAction : FileContextAction() + + @Name("AI Instruction") + val transformationMessage = JBTextField() + + @Name("Output File") + val outputFilename = JBTextField() } class UserSettings( @@ -49,26 +63,25 @@ class DocumentationCompilerAction : FileContextAction = CheckBoxList() val files = Files.walk(root) .filter { Files.isRegularFile(it) && !Files.isDirectory(it) } .toList().filterNotNull().toTypedArray() - filesToProcess.setItems(files.toMutableList()) { path -> - root?.relativize(path)?.toString() ?: path.toString() - } - files.forEach { path -> - filesToProcess.setItemSelected(path, true) - } val settingsUI = SettingsUI().apply { - filesToProcessScrollPane.setViewportView(filesToProcess) + filesToProcess.setItems(files.toMutableList()) { path -> + root?.relativize(path)?.toString() ?: path.toString() + } + files.forEach { path -> + filesToProcess.setItemSelected(path, true) + } + } + val dialog = DocumentationCompilerDialog(project, settingsUI) + dialog.show() + val result = dialog.isOK + val settings: UserSettings = dialog.userSettings + settings.filesToProcess = when { + result -> files.filter { path -> settingsUI.filesToProcess.isItemSelected(path) }.toList() + else -> listOf() } - val settings: UserSettings = UITools.showDialog2( - project, - settingsUI, - UserSettings::class.java, - "Compile Documentation" - ) { } - settings.filesToProcess = files.filter { path -> filesToProcess.isItemSelected(path) }.toList() //.map { path -> return@map root?.resolve(path) }.filterNotNull() return Settings(settings, project) } @@ -122,7 +135,7 @@ class DocumentationCompilerAction : FileContextAction settingsUI.filesToProcess.isItemSelected(path) } + } + } } + +private val CheckBoxList.items: List + get() { + val items = mutableListOf() + for (i in 0 until model.size) { + items.add(getItemAt(i)!!) + } + return items + } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/LineFilterChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/LineFilterChatAction.kt index 170a5e16..b1ee7377 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/LineFilterChatAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/LineFilterChatAction.kt @@ -4,6 +4,7 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -15,6 +16,7 @@ import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.chat.ChatServer import com.simiacryptus.skyenet.webui.chat.ChatSocketManager +import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.session.SocketManager import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory @@ -37,7 +39,7 @@ class LineFilterChatAction : BaseAction() { } agents[session] = object : ChatSocketManager( session = session, - model = AppSettingsState.instance.defaultChatModel(), + model = AppSettingsState.instance.smartModel.chatModel(), userInterfacePrompt = """ |# `$filename` | @@ -74,7 +76,7 @@ class LineFilterChatAction : BaseAction() { storage = ApplicationServices.dataStorageFactory(root), ) { override fun canWrite(user: User?): Boolean = true - override fun renderResponse(response: String): String { + override fun renderResponse(response: String, task: SessionTask): String { return renderMarkdown(response.split("\n").joinToString("\n") { when { // Is numeric, use line if in range diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/MultiDiffChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/MultiDiffChatAction.kt index 692e2568..2cebc564 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/MultiDiffChatAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/MultiDiffChatAction.kt @@ -4,8 +4,9 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage -import com.github.simiacryptus.aicoder.util.addApplyDiffLinks +import com.github.simiacryptus.aicoder.util.addApplyDiffLinks2 import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.command.WriteCommandAction @@ -17,6 +18,7 @@ import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.chat.ChatServer import com.simiacryptus.skyenet.webui.chat.ChatSocketManager +import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.session.SocketManager import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory @@ -58,7 +60,7 @@ class MultiDiffChatAction : BaseAction() { agents[session] = object : ChatSocketManager( session = session, - model = AppSettingsState.instance.defaultChatModel(), + model = AppSettingsState.instance.smartModel.chatModel(), userInterfacePrompt = """ | |${codeSummary()} @@ -92,8 +94,8 @@ class MultiDiffChatAction : BaseAction() { applicationClass = ApplicationServer::class.java, storage = ApplicationServices.dataStorageFactory(DiffChatAction.root), ) { - override fun renderResponse(response: String): String { - val html = renderMarkdown(addApplyDiffLinks(codeFiles, response) { newCodeMap -> + override fun renderResponse(response: String, task: SessionTask): String { + val html = renderMarkdown(addApplyDiffLinks2(codeFiles, response, handle = { newCodeMap -> newCodeMap.map { (path, newCode) -> val prev = codeFiles[path] if (prev != newCode) { @@ -113,7 +115,7 @@ class MultiDiffChatAction : BaseAction() { "" } } - }) + }, task = task)) return """
$html
""" } } @@ -152,17 +154,22 @@ class MultiDiffChatAction : BaseAction() { } } -fun Array.commonRoot() = this.reduce { a, b -> - if (a.startsWith(b)) b - else if (b.startsWith(a)) a - else { - val common = a.commonPrefixWith(b) - if (common == a) a - else if (common == b) b - else common.toAbsolutePath() +fun Array.commonRoot() : Path = when { + isEmpty() -> error("No paths") + size == 1 && first().toFile().isFile -> first().parent + size == 1 -> first() + else -> this.reduce { a, b -> + when { + a.startsWith(b) -> b + b.startsWith(a) -> a + else -> when (val common = a.commonPrefixWith(b)) { + a -> a + b -> b + else -> common.toAbsolutePath() + } + } } } - private fun Path.commonPrefixWith(b: Path): Path { val a = this val aParts = a.toAbsolutePath().toString().split(File.separator) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt index c60a13d2..41f13b2d 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project @@ -22,11 +23,11 @@ open class ReplaceOptionsAction : SelectionAction() { val proxy: VirtualAPI get() { return ChatProxy( - clazz = VirtualAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = VirtualAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ).create() } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/TaskRunnerAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/TaskRunnerAction.kt index 6a846fb7..8c0afb5a 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/TaskRunnerAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/TaskRunnerAction.kt @@ -2,22 +2,30 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer +import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.UITools -import com.github.simiacryptus.aicoder.util.addApplyDiffLinks +import com.github.simiacryptus.aicoder.util.addApplyDiffLinks2 import com.github.simiacryptus.aicoder.util.addSaveLinks import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.PlatformDataKeys +import com.intellij.openapi.actionSystem.PlatformDataKeys.VIRTUAL_FILE_ARRAY import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.isFile import com.simiacryptus.jopenai.API +import com.simiacryptus.jopenai.ApiModel +import com.simiacryptus.jopenai.ApiModel.Role import com.simiacryptus.jopenai.models.ChatModels +import com.simiacryptus.jopenai.util.ClientUtil.toContentList import com.simiacryptus.jopenai.util.JsonUtil.toJson -import com.simiacryptus.skyenet.AgentPatterns -import com.simiacryptus.skyenet.AgentPatterns.Retryable +import com.simiacryptus.skyenet.Acceptable +import com.simiacryptus.skyenet.AgentPatterns.displayMapInTabs +import com.simiacryptus.skyenet.Retryable +import com.simiacryptus.skyenet.TabbedDisplay import com.simiacryptus.skyenet.core.actors.* import com.simiacryptus.skyenet.core.platform.* import com.simiacryptus.skyenet.core.platform.ApplicationServices.clientManager import com.simiacryptus.skyenet.core.platform.file.DataStorage +import com.simiacryptus.skyenet.set import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.chat.ChatServer @@ -25,50 +33,15 @@ import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory import java.awt.Desktop +import java.util.* import java.util.concurrent.Future import java.util.concurrent.Semaphore import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.atomic.AtomicReference /** * The provided Kotlin code outlines a complex application designed for task planning and execution, integrating with a chat server and utilizing AI models for task breakdown, documentation generation, new file creation, file patching, and inquiries. The application leverages several components, including an IntelliJ plugin, a web UI, and AI-driven actors for processing user inputs and generating code or documentation. Below is a mermaid.js diagram that visualizes the high-level architecture and flow of this application. * - * ```mermaid - * graph TD - * A[TaskRunner] -->|initiates| B(AppServer) - * A -->|selects file| C(DataStorage) - * A -->|opens browser| D[Desktop] - * B -->|registers| E[TaskRunnerApp] - * E -->|handles user message| F[TaskRunnerAgent] - * E -->|initializes settings| G[Settings] - * F -->|processes user message| H[ActorSystem] - * H -->|executes actors based on task type| I[TaskBreakdownActor] - * H -->|executes actors based on task type| J[DocumentationGeneratorActor] - * H -->|executes actors based on task type| K[NewFileCreatorActor] - * H -->|executes actors based on task type| L[FilePatcherActor] - * H -->|executes actors based on task type| M[InquiryActor] - * I -->|generates tasks| N[TaskBreakdownResult] - * J -->|generates documentation| O[Documentation] - * K -->|creates new file| P[NewFile] - * L -->|patches existing file| Q[PatchedFile] - * M -->|provides inquiry response| R[InquiryResponse] - * N -->|is used for| S[Subsequent Task Planning] - * O -->|is linked to| T[Related Code] - * P -->|is added to| U[Codebase] - * Q -->|updates| V[Codebase] - * R -->|informs| W[Task Planning] - * - * style A fill:#f9f,stroke:#333,stroke-width:4px - * style B fill:#bbf,stroke:#333,stroke-width:2px - * style E fill:#bbf,stroke:#333,stroke-width:2px - * style F fill:#bbf,stroke:#333,stroke-width:2px - * style H fill:#bbf,stroke:#333,stroke-width:2px - * style I fill:#bfb,stroke:#333,stroke-width:2px - * style J fill:#bfb,stroke:#333,stroke-width:2px - * style K fill:#bfb,stroke:#333,stroke-width:2px - * style L fill:#bfb,stroke:#333,stroke-width:2px - * style M fill:#bfb,stroke:#333,stroke-width:2px - * ``` - * * ### Key Components and Flow: * * - **TaskRunner**: Initiates the task planning process, interacts with the `AppServer` for web UI, selects files via `DataStorage`, and opens a browser window for user interaction. @@ -114,8 +87,8 @@ class TaskRunnerApp( path = path, ) { data class Settings( - val model: ChatModels = ChatModels.GPT4Turbo, - val parsingModel: ChatModels = ChatModels.GPT35Turbo, + val model: ChatModels = AppSettingsState.instance.smartModel.chatModel(), + val parsingModel: ChatModels = AppSettingsState.instance.fastModel.chatModel(), val temperature: Double = 0.2, val budget: Double = 2.0, ) @@ -141,8 +114,8 @@ class TaskRunnerApp( dataStorage = dataStorage, api = api, ui = ui, - model = settings?.model ?: ChatModels.GPT4Turbo, - parsingModel = settings?.parsingModel ?: ChatModels.GPT35Turbo, + model = settings?.model ?: AppSettingsState.instance.smartModel.chatModel(), + parsingModel = settings?.parsingModel ?: AppSettingsState.instance.fastModel.chatModel(), temperature = settings?.temperature ?: 0.3, event = event, ).startProcess(userMessage = userMessage) @@ -222,6 +195,7 @@ class TaskRunnerAgent( Provide a clear file name suggestion based on the content and purpose of the file. Response should use one or more ``` code blocks to output file contents. + Triple backticks should be bracketed by newlines and an optional the language identifier. Each file should be preceded by a header that identifies the file being modified. Example: @@ -230,10 +204,12 @@ class TaskRunnerAgent( ### scripts/filename.js ```js + const b = 2; function exampleFunction() { return b + 1; } + ``` Continued text @@ -287,7 +263,7 @@ class TaskRunnerAgent( val event: AnActionEvent ) : ActorSystem( - actorMap.map { it.key.name to it.value.javaClass }.toMap(), + actorMap.map { it.key.name to it.value }.toMap(), dataStorage, user, session @@ -309,8 +285,15 @@ class TaskRunnerAgent( var task_dependencies: List? = null, val input_files: List? = null, val output_files: List? = null, + var state: TaskState? = null, ) + enum class TaskState { + Pending, + InProgress, + Completed, + } + enum class TaskType { TaskPlanning, Inquiry, @@ -320,10 +303,27 @@ class TaskRunnerAgent( } val root by lazy { - PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(event.dataContext)?.map { it.toFile.toPath() }?.toTypedArray() + VIRTUAL_FILE_ARRAY.getData(event.dataContext) + ?.map { it.toFile.toPath() }?.toTypedArray() ?.commonRoot()!! } - val virtualFiles by lazy { PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(event.dataContext) } + + val virtualFiles by lazy { expandFileList( + VIRTUAL_FILE_ARRAY.getData(event.dataContext) ?: arrayOf()) } + + private fun expandFileList(data: Array): Array { + return data.flatMap { + (when { + it.name.startsWith(".") -> arrayOf() + it.length > 1e6 -> arrayOf() + it.extension?.lowercase(Locale.getDefault()) in + setOf("jar", "zip", "class", "png", "jpg", "jpeg", "gif", "ico") -> arrayOf() + it.isDirectory -> expandFileList(it.children) + else -> arrayOf(it) + }).toList() + }.toTypedArray() + } + val codeFiles by lazy { mutableMapOf().apply { virtualFiles?.filter { it.isFile }?.forEach { file -> @@ -340,37 +340,86 @@ class TaskRunnerAgent( |Files: |${expandPaths(virtualFiles).joinToString("\n") { "* ${root.relativize(it)}" }} """.trimMargin() - val highLevelPlan = AgentPatterns.iterate( - input = userMessage, - heading = userMessage, - actor = taskBreakdownActor, - toInput = { - listOf( - eventStatus, - it + val task = ui.newTask() + val toInput = { it: String -> + listOf( + eventStatus, + it + ) + } + val highLevelPlan = Acceptable( + task = task, + userMessage = userMessage, + initialResponse = { it: String -> taskBreakdownActor.answer(toInput(it), api = api) }, + outputFn = { design: ParsedResponse -> + displayMapInTabs(mapOf( + "Text" to renderMarkdown(design.text), + "JSON" to renderMarkdown("```json\n${toJson(design.obj)}\n```"), +// "Flow" to renderMarkdown("## Task Graph\n```mermaid\n${buildMermaidGraph(design.obj.tasksByID?.toMutableMap() ?: mutableMapOf())}\n```") + ) ) }, - api = api, ui = ui, - outputFn = { design -> - AgentPatterns.displayMapInTabs( - mapOf( - "Text" to renderMarkdown(design.text), - "JSON" to renderMarkdown("```json\n${toJson(design.obj)}\n```") - ) + reviseResponse = { userMessages: List> -> + taskBreakdownActor.respond( + messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }.toTypedArray()), + input = toInput(userMessage), + api = api ) - } - ) - - val pool: ThreadPoolExecutor = clientManager.getPool(session, user, dataStorage) - val genState = GenState(highLevelPlan.obj.tasksByID?.toMutableMap() ?: mutableMapOf()) + }, + atomicRef = AtomicReference(), + semaphore = Semaphore(0), + heading = userMessage + ).call() try { - ui.newTask() - .complete(renderMarkdown("## Task Graph\n```mermaid\n${buildMermaidGraph(genState.subTasks)}\n```")) - while (genState.taskIds.isNotEmpty()) { - val taskId = genState.taskIds.removeAt(0) + val tasksByID = highLevelPlan.obj.tasksByID?.entries?.toTypedArray()?.associate { it.key to it.value } ?: mapOf() + val pool: ThreadPoolExecutor = clientManager.getPool(session, user, dataStorage) + val genState = GenState(tasksByID.toMutableMap()) + val diagramTask = ui.newTask() + val diagramBuffer = + diagramTask.add(renderMarkdown("## Task Dependency Graph\n```mermaid\n${buildMermaidGraph(genState.subTasks)}\n```")) + val taskTabs = object : TabbedDisplay(ui.newTask()) { + override fun renderTabButtons(): String { + diagramBuffer?.set(renderMarkdown("## Task Dependency Graph\n```mermaid\n${buildMermaidGraph(genState.subTasks)}\n```")) + diagramTask.complete() + return buildString { + append("
\n") + super.tabs.withIndex().forEach { (idx, t) -> + val (taskId, taskV) = t + val subTask = genState.tasksByDescription[taskId] + if (null == subTask) { + log.warn("Task tab not found: $taskId") + } + val isChecked = if (taskId in genState.taskIdProcessingQueue) "checked" else "" + log.debug("Task: '${subTask?.state}' ${System.identityHashCode(subTask)} '${taskId}' ") + val style = when(subTask?.state) { + TaskState.Completed -> " style='text-decoration: line-through;'" + null -> " style='opacity: 20%;'" + TaskState.Pending -> " style='opacity: 30%;'" + else -> "" + } + append("
\n") + } + append("
") + } + } + } + genState.taskIdProcessingQueue.forEach { taskId -> + val newTask = ui.newTask() + genState.uitaskMap[taskId] = newTask + val subtask = genState.subTasks[taskId] + val description = subtask?.description + log.debug("Creating task tab: $taskId ${System.identityHashCode(subtask)} $description") + taskTabs[description ?: taskId] = "
" + } + Thread.sleep(100) + while (genState.taskIdProcessingQueue.isNotEmpty()) { + val taskId = genState.taskIdProcessingQueue.removeAt(0) val subTask = genState.subTasks[taskId] ?: throw RuntimeException("Task not found: $taskId") + genState.taskFutures[taskId] = pool.submit { + subTask.state = TaskState.Pending + log.debug("Awaiting dependencies: ${subTask.task_dependencies?.joinToString(", ") ?: ""}") subTask.task_dependencies ?.associate { it to genState.taskFutures[it] } ?.forEach { (id, future) -> @@ -380,13 +429,16 @@ class TaskRunnerAgent( log.warn("Error", e) } } - genState.taskFutures[taskId] = pool.submit { + subTask.state = TaskState.InProgress + log.debug("Running task: ${System.identityHashCode(subTask)} ${subTask.description}") runTask( taskId = taskId, subTask = subTask, userMessage = userMessage, highLevelPlan = highLevelPlan, - genState = genState + genState = genState, + task = genState.uitaskMap.get(taskId) ?: ui.newTask(), + taskTabs = taskTabs ) } } @@ -399,7 +451,7 @@ class TaskRunnerAgent( } } catch (e: Throwable) { log.warn("Error during incremental code generation process", e) - ui.newTask().error(ui, e) + task.error(ui, e) } } @@ -409,10 +461,12 @@ class TaskRunnerAgent( data class GenState( val subTasks: MutableMap, - val taskIds: MutableList = executionOrder(subTasks).toMutableList(), - val replyText: MutableMap = mutableMapOf(), + val tasksByDescription: MutableMap = subTasks.entries.toTypedArray().associate { it.value.description to it.value }.toMutableMap(), + val taskIdProcessingQueue: MutableList = executionOrder(subTasks).toMutableList(), + val taskResult: MutableMap = mutableMapOf(), val completedTasks: MutableList = mutableListOf(), val taskFutures: MutableMap> = mutableMapOf(), + val uitaskMap: MutableMap = mutableMapOf(), ) private fun runTask( @@ -421,9 +475,11 @@ class TaskRunnerAgent( userMessage: String, highLevelPlan: ParsedResponse, genState: GenState, - task: SessionTask = ui.newTask(), + task: SessionTask, + taskTabs: TabbedDisplay, ) { try { + taskTabs.update() val dependencies = subTask.task_dependencies?.toMutableSet() ?: mutableSetOf() dependencies += getAllDependencies(subTask, genState.subTasks) val priorCode = dependencies @@ -431,7 +487,7 @@ class TaskRunnerAgent( """ |# $dependency | - |${genState.replyText[dependency] ?: ""} + |${genState.taskResult[dependency] ?: ""} """.trimMargin() } val inputFileCode = subTask.input_files?.joinToString("\n\n\n") { @@ -536,14 +592,15 @@ class TaskRunnerAgent( TaskType.TaskPlanning -> { taskPlanning( - subTask, - userMessage, - highLevelPlan, - priorCode, - inputFileCode, - genState, - taskId, - task + subTask = subTask, + userMessage = userMessage, + highLevelPlan = highLevelPlan, + priorCode = priorCode, + inputFileCode = inputFileCode, + genState = genState, + taskId = taskId, + task = task, + taskTabs = taskTabs, ) } @@ -554,6 +611,9 @@ class TaskRunnerAgent( task.error(ui, e) } finally { genState.completedTasks.add(taskId) + subTask.state = TaskState.Completed + log.debug("Completed task: $taskId ${System.identityHashCode(subTask)}") + taskTabs.update() } } @@ -568,6 +628,7 @@ class TaskRunnerAgent( taskId: String, onComplete: () -> Unit ) { + val process = { sb : StringBuilder -> val codeResult = newFileCreatorActor.answer( listOf( @@ -578,13 +639,15 @@ class TaskRunnerAgent( subTask.description ?: "", ), api ) - genState.replyText[taskId] = codeResult - renderMarkdown(ui.socketManager.addSaveLinks(codeResult) { path, newCode -> + genState.taskResult[taskId] = codeResult + renderMarkdown(ui.socketManager.addSaveLinks(codeResult, task) { path, newCode -> val prev = codeFiles[path] if (prev != newCode) { codeFiles[path] = newCode val bytes = newCode.toByteArray(Charsets.UTF_8) - task.complete("$path Updated") + task.complete("$path Created") + } else { + task.complete("No changes to $path") } }) + accept(sb) { task.complete() @@ -619,8 +682,8 @@ class TaskRunnerAgent( subTask.description ?: "", ), api ) - genState.replyText[taskId] = codeResult - renderMarkdown(ui.socketManager.addApplyDiffLinks(codeFiles, codeResult) { newCodeMap -> + genState.taskResult[taskId] = codeResult + renderMarkdown(ui.socketManager.addApplyDiffLinks2(codeFiles, codeResult, handle = { newCodeMap -> newCodeMap.forEach { (path, newCode) -> val prev = codeFiles[path] if (prev != newCode) { @@ -635,7 +698,7 @@ class TaskRunnerAgent( ) } } - }) + accept(sb) { + }, task = task)) + accept(sb) { task.complete() onComplete() } @@ -666,7 +729,7 @@ class TaskRunnerAgent( inputFileCode, ), api ) - genState.replyText[taskId] = docResult + genState.taskResult[taskId] = docResult renderMarkdown("## Generated Documentation\n$docResult") + accept(sb) { task.complete() onComplete() @@ -708,27 +771,36 @@ class TaskRunnerAgent( taskId: String, task: SessionTask ) { - val inquiryResult = AgentPatterns.iterate( - input = "Expand ${subTask.description ?: ""}", - heading = "Expand ${subTask.description ?: ""}", - actor = inquiryActor, - toInput = { - listOf( - userMessage, - highLevelPlan.text, - priorCode, - inputFileCode, - it, + val input1 = "Expand ${subTask.description ?: ""}" + val toInput = { it: String -> + listOf( + userMessage, + highLevelPlan.text, + priorCode, + inputFileCode, + it, + ) + } + val inquiryResult = Acceptable( + task = task, + userMessage = input1, + initialResponse = { it: String -> inquiryActor.answer(toInput(it), api = api) }, + outputFn = { design: String -> + renderMarkdown(design) + }, + ui = ui, + reviseResponse = { userMessages: List> -> + inquiryActor.respond( + messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }.toTypedArray()), + input = toInput(input1), + api = api ) }, - api = api, - ui = ui, - outputFn = { design -> - renderMarkdown(design) - } - ) - genState.replyText[taskId] = inquiryResult - task.complete(renderMarkdown("## Generated Inquiry Response\n$inquiryResult")) + atomicRef = AtomicReference(), + semaphore = Semaphore(0), + heading = "Expand ${subTask.description ?: ""}" + ).call() + genState.taskResult[taskId] = inquiryResult } private fun taskPlanning( @@ -739,35 +811,49 @@ class TaskRunnerAgent( inputFileCode: String, genState: GenState, taskId: String, - task: SessionTask + task: SessionTask, + taskTabs: TabbedDisplay ) { - val subPlan = AgentPatterns.iterate( - input = "Expand ${subTask.description ?: ""}", - heading = "Expand ${subTask.description ?: ""}", - actor = taskBreakdownActor, - toInput = { - listOf( - userMessage, - highLevelPlan.text, - priorCode, - inputFileCode, - it - ) - }, - api = api, - ui = ui, - outputFn = { design -> - AgentPatterns.displayMapInTabs( - mapOf( + val input1 = "Expand ${subTask.description ?: ""}" + val toInput = { it: String -> + listOf( + userMessage, + highLevelPlan.text, + priorCode, + inputFileCode, + it + ) + } + val subPlan = Acceptable( + task = task, + userMessage = input1, + initialResponse = { it: String -> taskBreakdownActor.answer(toInput(it), api = api) }, + outputFn = { design: ParsedResponse -> + displayMapInTabs(mapOf( "Text" to renderMarkdown(design.text), - "JSON" to renderMarkdown("```json\n${toJson(design.obj)}\n```") - ) + "JSON" to renderMarkdown("```json\n${toJson(design.obj)}\n```"), + )) + }, + ui = ui, + reviseResponse = { userMessages: List> -> + taskBreakdownActor.respond( + messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }.toTypedArray()), + input = toInput(input1), + api = api ) }, - task = task - ) - genState.replyText[taskId] = subPlan.text + atomicRef = AtomicReference(), + semaphore = Semaphore(0), + heading = "Expand ${subTask.description ?: ""}" + ).call() + genState.taskResult[taskId] = subPlan.text var newTasks = subPlan.obj.tasksByID + newTasks?.forEach { + val newTask = ui.newTask() + genState.uitaskMap[it.key] = newTask + genState.tasksByDescription[it.value.description] = it.value + taskTabs[it.value.description ?: it.key] = "
" + } val conflictingKeys = newTasks?.keys?.intersect(genState.subTasks.keys) newTasks = newTasks?.entries?.associate { (key, value) -> (when { @@ -780,8 +866,9 @@ class TaskRunnerAgent( } }) } + log.debug("New Tasks: ${newTasks?.keys}") genState.subTasks.putAll(newTasks ?: emptyMap()) - executionOrder(newTasks ?: emptyMap()).reversed().forEach { genState.taskIds.add(0, it) } + executionOrder(newTasks ?: emptyMap()).reversed().forEach { genState.taskIdProcessingQueue.add(0, it) } genState.subTasks.values.forEach { it.task_dependencies = it.task_dependencies?.map { dep -> when { @@ -790,7 +877,6 @@ class TaskRunnerAgent( } } } - task.complete(renderMarkdown("## Task Dependency Graph\n```mermaid\n${buildMermaidGraph(genState.subTasks)}\n```")) } private fun getAllDependencies(subTask: Task, subTasks: MutableMap): List { @@ -816,24 +902,38 @@ class TaskRunnerAgent( private fun buildMermaidGraph(subTasks: Map): String { val graphBuilder = StringBuilder("graph TD;\n") - val escapeMermaidCharacters: (String) -> String = { input -> - input.replace("\"", "\\\"") - .replace("[", "\\[") - .replace("]", "\\]") - .replace("(", "\\(") - .replace(")", "\\)") - } subTasks.forEach { (taskId, task) -> - val taskId = taskId.replace(" ", "_") + val sanitizedTaskId = sanitizeForMermaid(taskId) + val taskType = task.taskType?.name ?: "Unknown" val escapedDescription = escapeMermaidCharacters(task.description ?: "") - graphBuilder.append(" ${taskId}[\"${escapedDescription}\"];\n") + graphBuilder.append(" ${sanitizedTaskId}[$escapedDescription]:::$taskType;\n") task.task_dependencies?.forEach { dependency -> - graphBuilder.append(" ${dependency.replace(" ", "_")} --> ${taskId};\n") + val sanitizedDependency = sanitizeForMermaid(dependency) + graphBuilder.append(" ${sanitizedDependency} --> ${sanitizedTaskId};\n") } } + graphBuilder.append(" classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px;\n") + graphBuilder.append(" classDef NewFile fill:lightblue,stroke:#333,stroke-width:2px;\n") + graphBuilder.append(" classDef EditFile fill:lightgreen,stroke:#333,stroke-width:2px;\n") + graphBuilder.append(" classDef Documentation fill:lightyellow,stroke:#333,stroke-width:2px;\n") + graphBuilder.append(" classDef Inquiry fill:orange,stroke:#333,stroke-width:2px;\n") + graphBuilder.append(" classDef TaskPlanning fill:lightgrey,stroke:#333,stroke-width:2px;\n") return graphBuilder.toString() } + private fun sanitizeForMermaid(input: String): String { + return input.replace(" ", "_") + .replace("\"", "\\\"") + .replace("[", "\\[") + .replace("]", "\\]") + .replace("(", "\\(") + .replace(")", "\\)") + } + + private fun escapeMermaidCharacters(input: String): String { + return input + } + enum class ActorTypes { TaskBreakdown, DocumentationGenerator, diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/WebDevAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/WebDevAction.kt index 6b5eec4a..319486bb 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/WebDevAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/WebDevAction.kt @@ -4,21 +4,20 @@ import com.github.simiacryptus.aicoder.ApplicationEvents import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.actions.dev.AppServer import com.github.simiacryptus.aicoder.util.UITools -import com.github.simiacryptus.aicoder.util.addApplyDiffLinks +import com.github.simiacryptus.aicoder.util.addApplyDiffLinks2 import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.vfs.VirtualFile import com.simiacryptus.jopenai.API import com.simiacryptus.jopenai.ApiModel +import com.simiacryptus.jopenai.ApiModel.Role import com.simiacryptus.jopenai.describe.Description import com.simiacryptus.jopenai.models.ChatModels import com.simiacryptus.jopenai.proxy.ValidatedObject import com.simiacryptus.jopenai.util.ClientUtil.toContentList import com.simiacryptus.jopenai.util.JsonUtil +import com.simiacryptus.skyenet.Acceptable import com.simiacryptus.skyenet.AgentPatterns -import com.simiacryptus.skyenet.core.actors.ActorSystem -import com.simiacryptus.skyenet.core.actors.BaseActor -import com.simiacryptus.skyenet.core.actors.ParsedActor -import com.simiacryptus.skyenet.core.actors.SimpleActor +import com.simiacryptus.skyenet.core.actors.* import com.simiacryptus.skyenet.core.platform.* import com.simiacryptus.skyenet.core.platform.file.DataStorage import com.simiacryptus.skyenet.webui.application.ApplicationInterface @@ -30,7 +29,9 @@ import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown import org.slf4j.LoggerFactory import java.awt.Desktop import java.io.File +import java.util.concurrent.Semaphore import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference val VirtualFile.toFile: File get() = File(this.path) @@ -177,7 +178,7 @@ class WebDevAction : BaseAction() { model = model ), ), - ) : ActorSystem(actorMap.map { it.key.name to it.value.javaClass }.toMap(), dataStorage, user, session) { + ) : ActorSystem(actorMap.map { it.key.name to it.value }.toMap(), dataStorage, user, session) { enum class ActorTypes { HtmlCodingActor, JavascriptCodingActor, @@ -199,19 +200,34 @@ class WebDevAction : BaseAction() { fun start( userMessage: String, ) { - val architectureResponse = AgentPatterns.iterate( - input = userMessage, - heading = userMessage, - actor = architectureDiscussionActor, - toInput = { listOf(it) }, - api = api, + val task = ui.newTask() + val toInput = { it: String -> listOf(it) } + val architectureResponse = Acceptable( + task = task, + userMessage = userMessage, + initialResponse = { it: String -> architectureDiscussionActor.answer(toInput(it), api = api) }, + outputFn = { design: ParsedResponse -> + // renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj)}\n```") + AgentPatterns.displayMapInTabs( + mapOf( + "Text" to renderMarkdown(design.text), + "JSON" to renderMarkdown("```json\n${JsonUtil.toJson(design.obj)}\n```"), + ) + ) + }, ui = ui, - outputFn = { design -> - renderMarkdown("${design.text}\n\n```json\n${JsonUtil.toJson(design.obj)}\n```") - } - ) + reviseResponse = { userMessages: List> -> + architectureDiscussionActor.respond( + messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }.toTypedArray()), + input = toInput(userMessage), + api = api + ) + }, + atomicRef = AtomicReference(), + semaphore = Semaphore(0), + heading = userMessage + ).call() - val task = ui.newTask() try { val toolSpecs = tools.map { ToolServlet.tools.find { t -> t.path == it } } .joinToString("\n\n") { it?.let { JsonUtil.toJson(it.openApiDescription) } ?: "" } @@ -289,10 +305,9 @@ class WebDevAction : BaseAction() { //val task = ui.newTask() return task.complete( renderMarkdown( - ui.socketManager.addApplyDiffLinks( + ui.socketManager.addApplyDiffLinks2( codeFiles, - design - ) { newCodeMap -> + design, handle = { newCodeMap -> newCodeMap.forEach { (path, newCode) -> val prev = codeFiles[path] if (prev != newCode) { @@ -307,7 +322,8 @@ class WebDevAction : BaseAction() { ) } } - }) + }, task = task) + ) ) } try { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt index c777a1a6..4896b8a8 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt @@ -2,6 +2,7 @@ package com.github.simiacryptus.aicoder.actions.markdown import com.github.simiacryptus.aicoder.actions.SelectionAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.actionSystem.ActionGroup @@ -54,11 +55,11 @@ class MarkdownImplementActionGroup : ActionGroup() { private fun getProxy(): ConversionAPI { return ChatProxy( - clazz = ConversionAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - temperature = AppSettingsState.instance.temperature, - deserializerRetries = 5 + clazz = ConversionAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + temperature = AppSettingsState.instance.temperature, + deserializerRetries = 5 ).create() } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt index 48d95bf5..712773fe 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt @@ -2,6 +2,7 @@ import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.getIndent @@ -30,10 +31,10 @@ class MarkdownListAction : BaseAction() { val proxy: ListAPI get() { val chatProxy = ChatProxy( - clazz = ListAPI::class.java, - api = api, - model = AppSettingsState.instance.defaultChatModel(), - deserializerRetries = 5, + clazz = ListAPI::class.java, + api = api, + model = AppSettingsState.instance.smartModel.chatModel(), + deserializerRetries = 5, ) chatProxy.addExample( returnValue = ListAPI.Items( diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/ActionSettingsRegistry.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/ActionSettingsRegistry.kt index 03682ca2..a0b40b51 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/ActionSettingsRegistry.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/ActionSettingsRegistry.kt @@ -11,10 +11,11 @@ import java.util.stream.Collectors class ActionSettingsRegistry { val actionSettings: MutableMap = HashMap() - private val version = 2.0075 + private val version = 2.0080 // Increment this value to force a reload of all actions private fun updateActionConfig(action: AnAction, code: String, language: String) { val actionConfig = this.getActionConfig(action) + log.info("Updating action config for action: ${action.javaClass.name}, language: $language") actionConfig.language = language actionConfig.isDynamic = false with(action) { @@ -39,6 +40,7 @@ class ActionSettingsRegistry { val localCode = actionConfig.file.readText().dropWhile { !it.isLetter() } if (!localCode.equals(code)) { try { + log.info("Handling dynamic action config for action: ${action.javaClass.name}") val element = actionConfig.buildAction(localCode) actionConfig.version = version actionConfig.file.writeText(code) @@ -55,6 +57,7 @@ class ActionSettingsRegistry { false } if (canLoad) { + log.info("Can load class for action: ${actionConfig.id}, updating code.") actionConfig.file.writeText(code) actionConfig.version = version } else { @@ -66,6 +69,7 @@ class ActionSettingsRegistry { val children = superChildren.toList().toMutableList() children.toTypedArray().forEach { action -> val language = "kt" + log.info("Editing action: ${action.javaClass.name}, language: $language") val code: String? = load(action.javaClass, language) if (null != code) { try { @@ -81,6 +85,7 @@ class ActionSettingsRegistry { this.getDynamicActions().forEach { try { if (!it.file.exists()) return@forEach + log.info("Adding dynamic action: ${it.id}") if (!it.enabled) return@forEach val element = it.buildAction(it.file.readText()) children.add(element) @@ -104,6 +109,7 @@ class ActionSettingsRegistry { data class ActionSettings( val id: String, // Static property var enabled: Boolean = true, // User settable + // Adding logging within the buildAction method to log the action building process var displayText: String? = null, // User settable var version: Double? = null, // System property var isDynamic: Boolean = false, // Static property @@ -116,6 +122,7 @@ class ActionSettingsRegistry { val newClassName = this.className + "_" + Integer.toHexString(code.hashCode()) with( actionCache.getOrPut("$packageName.$newClassName") { + log.info("Compiling code for new action: $newClassName") (compile( code.replace( ("""(? } catch (e: Throwable) { @@ -166,6 +174,8 @@ class ActionSettingsRegistry { return language == other.language } + + override fun hashCode(): Int { var result = id.hashCode() result = 31 * result + enabled.hashCode() @@ -180,6 +190,7 @@ class ActionSettingsRegistry { private fun getActionConfig(action: AnAction): ActionSettings { return actionSettings.getOrPut(action.javaClass.name) { val actionConfig = ActionSettings(action.javaClass.name) + log.info("Creating new action config for action: ${action.javaClass.name}") actionConfig.displayText = action.templatePresentation.text actionConfig } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt index a5a6c95e..7537f6fa 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt @@ -13,7 +13,6 @@ import com.intellij.ui.components.JBTextField import com.intellij.ui.table.JBTable import com.simiacryptus.jopenai.models.ChatModels import com.simiacryptus.skyenet.core.platform.ApplicationServices -import org.slf4j.LoggerFactory import java.awt.event.ActionEvent import java.io.FileOutputStream import javax.swing.AbstractAction @@ -51,7 +50,11 @@ class AppSettingsComponent : com.intellij.openapi.Disposable { @Suppress("unused") @Name("Model") - val modelName = ComboBox() + val smartModel = ComboBox() + + @Suppress("unused") + @Name("Model") + val fastModel = ComboBox() @Suppress("unused") @Name("Enable API Log") @@ -135,8 +138,14 @@ class AppSettingsComponent : com.intellij.openapi.Disposable { // this.modelName.addItem(ChatModels.GPT35Turbo.modelName) // this.modelName.addItem(ChatModels.GPT4.modelName) // this.modelName.addItem(ChatModels.GPT4Turbo.modelName) - ChatModels.values().forEach { this.modelName.addItem(it.key) } - this.modelName.isEditable = true + ChatModels.values().forEach { +// this.modelName.addItem(it.key) + this.smartModel.addItem(it.value.modelName) + this.fastModel.addItem(it.value.modelName) + } +// this.modelName.isEditable = true + this.smartModel.isEditable = true + this.fastModel.isEditable = true } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsState.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsState.kt index d13f9e21..470bde98 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsState.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsState.kt @@ -13,7 +13,8 @@ import java.io.File @State(name = "org.intellij.sdk.settings.AppSettingsState", storages = [Storage("SdkSettingsPlugin.xml")]) data class AppSettingsState( var temperature: Double = 0.1, - var modelName: String = ChatModels.GPT35Turbo.modelName, + var smartModel: String = ChatModels.GPT35Turbo.modelName, + var fastModel: String = ChatModels.GPT35Turbo.modelName, var listeningPort: Int = 8081, var listeningEndpoint: String = "localhost", var humanLanguage: String = "English", @@ -33,9 +34,8 @@ data class AppSettingsState( val fileActions = ActionSettingsRegistry() private val recentCommands = mutableMapOf() - fun defaultChatModel(): ChatModels = ChatModels.values().entries.firstOrNull { - it.value.modelName == modelName || it.key == modelName - }?.value ?: throw IllegalArgumentException("Unknown model: $modelName") + fun defaultSmartModel() = smartModel.chatModel() + fun defaultFastModel() = fastModel.chatModel() @JsonIgnore override fun getState() = SimpleEnvelope(JsonUtil.toJson(this)) @@ -75,5 +75,11 @@ data class AppSettingsState( val instance: AppSettingsState by lazy { ApplicationManager.getApplication()?.getService(AppSettingsState::class.java) ?: AppSettingsState() } + + fun String.chatModel(): ChatModels { + return ChatModels.values().entries.firstOrNull { + it.value.modelName == this || it.key == this + }?.value ?: throw IllegalArgumentException("Unknown model: ${this}") + } } } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/StaticAppSettingsConfigurable.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/StaticAppSettingsConfigurable.kt index e9968cb1..1b17d811 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/StaticAppSettingsConfigurable.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/StaticAppSettingsConfigurable.kt @@ -37,8 +37,12 @@ class StaticAppSettingsConfigurable : AppSettingsConfigurable() { add(JPanel(BorderLayout()).apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) add(JPanel(FlowLayout(FlowLayout.LEFT)).apply { - add(JLabel("Model:")) - add(component.modelName) + add(JLabel("Smart Model:")) + add(component.smartModel) + }) + add(JPanel(FlowLayout(FlowLayout.LEFT)).apply { + add(JLabel("Fast Model:")) + add(component.fastModel) }) add(JPanel(FlowLayout(FlowLayout.LEFT)).apply { add(JLabel("Temperature:")) @@ -112,7 +116,9 @@ class StaticAppSettingsConfigurable : AppSettingsConfigurable() { component.listeningPort.text = settings.listeningPort.toString() component.listeningEndpoint.text = settings.listeningEndpoint component.suppressErrors.isSelected = settings.suppressErrors - component.modelName.selectedItem = settings.modelName +// component.modelName.selectedItem = settings.modelName + component.fastModel.selectedItem = settings.fastModel + component.smartModel.selectedItem = settings.smartModel component.apiLog.isSelected = settings.apiLog component.devActions.isSelected = settings.devActions component.editRequests.isSelected = settings.editRequests @@ -136,7 +142,9 @@ class StaticAppSettingsConfigurable : AppSettingsConfigurable() { settings.listeningPort = component.listeningPort.text.safeInt() settings.listeningEndpoint = component.listeningEndpoint.text settings.suppressErrors = component.suppressErrors.isSelected - settings.modelName = component.modelName.selectedItem as String +// settings.modelName = component.modelName.selectedItem as String + settings.fastModel = component.fastModel.selectedItem as String + settings.smartModel = component.smartModel.selectedItem as String settings.apiLog = component.apiLog.isSelected settings.devActions = component.devActions.isSelected settings.editRequests = component.editRequests.isSelected diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/ui/ModelSelectionWidgetFactory.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/ui/ModelSelectionWidgetFactory.kt index 5c875cc5..8c80caca 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/ui/ModelSelectionWidgetFactory.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/ui/ModelSelectionWidgetFactory.kt @@ -1,6 +1,7 @@ package com.github.simiacryptus.aicoder.ui import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel import com.intellij.openapi.project.Project import com.intellij.openapi.ui.popup.JBPopup import com.intellij.openapi.ui.popup.JBPopupFactory @@ -16,89 +17,96 @@ import java.awt.BorderLayout import javax.swing.* class ModelSelectionWidgetFactory : StatusBarWidgetFactory { - companion object { - } - - class ModelSelectionWidget : StatusBarWidget, StatusBarWidget.MultipleTextValuesPresentation { - private var statusBar: StatusBar? = null - private var activeModel: String = AppSettingsState.instance.defaultChatModel().modelName - var models: List = models() + class ModelSelectionWidget : StatusBarWidget, StatusBarWidget.MultipleTextValuesPresentation { - init { - AppSettingsState.instance.addOnSettingsLoadedListener { - models = models() - statusBar?.updateWidget(ID()) - } - } - fun models() = ChatModels.values().filter { - AppSettingsState.instance.apiKey?.filter { it.value.isNotBlank() }?.keys?.contains(it.value.provider.name) ?: false - }.map { it.value }.toList() + private var statusBar: StatusBar? = null + private var activeModel: String = AppSettingsState.instance.smartModel.chatModel().modelName + var models: List = models() - override fun ID(): String { - return "ModelSelectionComponent" + init { + AppSettingsState.instance.addOnSettingsLoadedListener { + models = models() + statusBar?.updateWidget(ID()) } + } - override fun getPresentation(): StatusBarWidget.WidgetPresentation { - return this - } - - override fun install(statusBar: StatusBar) { - this.statusBar = statusBar - } + fun models() = ChatModels.values().filter { + AppSettingsState.instance.apiKey?.filter { it.value.isNotBlank() }?.keys?.contains(it.value.provider.name) + ?: false + }.map { it.value }.toList() - override fun dispose() { - //connection?.disconnect() + override fun ID(): String { + return "ModelSelectionComponent" + } + + override fun getPresentation(): StatusBarWidget.WidgetPresentation { + return this + } + + override fun install(statusBar: StatusBar) { + this.statusBar = statusBar + } + + override fun dispose() { + //connection?.disconnect() + } + + override fun getTooltipText(): String { + return "Current active model" + } + + override fun getSelectedValue(): String { + return activeModel + } + + private fun getRenderer(): ListCellRenderer = object : SimpleListCellRenderer() { + override fun customize( + list: JList, + value: String?, + index: Int, + selected: Boolean, + hasFocus: Boolean + ) { + text = value // Here you can add more customization if needed } - - override fun getTooltipText(): String { - return "Current active model" + } + + override fun getPopup(): JBPopup { + val inputField = JTextField() + val listModel = CollectionListModel(models.map { it.modelName }) + val list = JBList(listModel) + list.cellRenderer = getRenderer() + + val panel = JPanel(BorderLayout()) + panel.add(inputField, BorderLayout.NORTH) + panel.add(JScrollPane(list), BorderLayout.CENTER) + + val popup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, inputField) + .setRequestFocus(true) + .setCancelOnClickOutside(true) + .createPopup() + + list.addListSelectionListener { + val selectedValue = list.selectedValue + activeModel = selectedValue + AppSettingsState.instance.smartModel = selectedValue + statusBar?.updateWidget(ID()) + popup.closeOk(null) } - override fun getSelectedValue(): String { - return activeModel + inputField.addActionListener { + val inputValue = inputField.text + if (inputValue.isNotEmpty()) { + activeModel = inputValue + AppSettingsState.instance.smartModel = inputValue + statusBar?.updateWidget(ID()) + popup.closeOk(null) + } } - private fun getRenderer(): ListCellRenderer = object : SimpleListCellRenderer() { - override fun customize(list: JList, value: String?, index: Int, selected: Boolean, hasFocus: Boolean) { - text = value // Here you can add more customization if needed - } - } - override fun getPopup(): JBPopup { - val inputField = JTextField() - val listModel = CollectionListModel(models.map { it.modelName }) - val list = JBList(listModel) - list.cellRenderer = getRenderer() - - val panel = JPanel(BorderLayout()) - panel.add(inputField, BorderLayout.NORTH) - panel.add(JScrollPane(list), BorderLayout.CENTER) - - val popup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, inputField) - .setRequestFocus(true) - .setCancelOnClickOutside(true) - .createPopup() - - list.addListSelectionListener { - val selectedValue = list.selectedValue - activeModel = selectedValue - AppSettingsState.instance.modelName = selectedValue - statusBar?.updateWidget(ID()) - popup.closeOk(null) - } - - inputField.addActionListener { - val inputValue = inputField.text - if (inputValue.isNotEmpty()) { - activeModel = inputValue - AppSettingsState.instance.modelName = inputValue - statusBar?.updateWidget(ID()) - popup.closeOk(null) - } - } - - return popup - } + return popup + } } override fun getId(): String { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt index 6bd210f8..56557fe5 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt @@ -31,7 +31,6 @@ import com.intellij.util.ui.FormBuilder import com.simiacryptus.jopenai.OpenAIClient import com.simiacryptus.jopenai.exceptions.ModerationException import com.simiacryptus.jopenai.models.APIProvider -import com.simiacryptus.jopenai.util.StringUtil import org.jdesktop.swingx.JXButton import org.slf4j.LoggerFactory import java.awt.* @@ -90,9 +89,11 @@ object UITools { event: AnActionEvent, request: Supplier, ) { + log.debug("Starting redoableTask with event: ${event}, request: ${request}") Futures.addCallback(pool.submit { request.get() }, futureCallback(event, request), pool) + log.debug("Submitted redoableTask for execution") } private fun futureCallback( @@ -114,18 +115,17 @@ object UITools { event: AnActionEvent, request: Supplier, undo: Runnable, - ): Runnable { - return Runnable { - Futures.addCallback( - pool.submit { - WriteCommandAction.runWriteCommandAction(event.project) { undo?.run() } - request.get() - }, futureCallback(event, request), pool - ) - } + ): Runnable = Runnable { + Futures.addCallback( + pool.submit { + WriteCommandAction.runWriteCommandAction(event.project) { undo?.run() } + request.get() + }, futureCallback(event, request), pool + ) } fun replaceString(document: Document, startOffset: Int, endOffset: Int, newText: CharSequence): Runnable { + log.debug("Invoking replaceString with startOffset: $startOffset, endOffset: $endOffset, newText: $newText") val oldText: CharSequence = document.getText(TextRange(startOffset, endOffset)) document.replaceString(startOffset, endOffset, newText) logEdit( @@ -140,6 +140,7 @@ object UITools { ) return Runnable { val verifyTxt = document.getText(TextRange(startOffset, startOffset + newText.length)) + log.debug("Verifying text after replaceString: expected: $newText, actual: $verifyTxt") if (verifyTxt != newText) { val msg = String.format( "The text range from %d to %d does not match the expected text \"%s\" and is instead \"%s\"", @@ -148,6 +149,7 @@ object UITools { newText, verifyTxt ) + log.error("Verification failed after replaceString: $msg") throw IllegalStateException(msg) } document.replaceString(startOffset, startOffset + newText.length, oldText) @@ -240,32 +242,6 @@ object UITools { if (uiVal is JScrollPane) { uiVal = uiVal.viewport.view } -// // Handle JBList bound to ArrayList -// if (uiVal is JBList<*> && ArrayList::class.java.isAssignableFrom(settingsField.returnType.javaType as Class<*>) && settingsField.returnType.arguments[0].type?.javaType?.typeName == "java.nio.file.Path") { -// val model = uiVal.model -// newSettingsValue = ArrayList().apply { -// for (i in 0 until model.size) { -// val element = model.getElementAt(i) -// if (element is String) { -// add(Paths.get(element)) -// } -// } -// } -// } -// // Handle JBList bound to List -// if (uiVal is JBList<*> && List::class.java.isAssignableFrom(settingsField.returnType.javaClass)) { -// newSettingsValue = uiVal.model.elements -// } -// // Handle CheckBoxList bound to List -// if (uiVal is CheckBoxList<*> && List::class.java.isAssignableFrom(settingsField.returnType.javaClass)) { -// val model = uiVal.model -// val checkBoxListValues = ArrayList() -// for (i in 0 until model.size) { -// checkBoxListValues.add( -// model.getElementAt(i)?.let { item -> item to uiVal.isItemSelected(item as Nothing?) }) -// } -// newSettingsValue = checkBoxListValues -// } when (settingsField.returnType.javaType.typeName) { "java.lang.String" -> if (uiVal is JTextComponent) { newSettingsValue = uiVal.text @@ -337,36 +313,6 @@ object UITools { if (uiVal is JScrollPane) { uiVal = uiVal.viewport.view } -// // Handle JBList bound to ArrayList -// if (uiVal is JBList<*> && settingsVal is List<*> && settingsVal.all { it is Path }) { -// val listModel = DefaultListModel() -// settingsVal.forEach { path -> -// if (path is Path) { -// listModel.addElement(path.toString()) -// } -// } -// uiVal.model = listModel -// } -// // Handle JBList bound to List -// if (uiVal is JBList<*> && settingsVal is List<*>) { -// val listModel = DefaultListModel() -// settingsVal.forEach { listModel.addElement(it) } -// uiVal.model = listModel -// } -// // Handle CheckBoxList bound to List> -// val checkBoxListValues = ArrayList>() -// if (uiVal is CheckBoxList<*> && settingsVal is List<*>) { -// val listModel = DefaultListModel() -// settingsVal.forEach { item -> -// if (item is Pair<*, *>) { -// listModel.addElement(item.first) -// uiVal.setItemSelected(item.first as Nothing?, item.second as Boolean) -// } -// } -// settingsVal.forEach { listModel.addElement(it) } -// @Suppress("UNCHECKED_CAST") -// uiVal.model = listModel as ListModel -// } when (settingsField.returnType.javaType.typeName) { "java.lang.String" -> if (uiVal is JTextComponent) { uiVal.text = settingsVal.toString() @@ -598,31 +544,22 @@ object UITools { configClass: Class, title: String = "Generate Project", onComplete: (C) -> Unit = { _ -> }, - ) = showDialog(project, uiClass, configClass.getConstructor().newInstance(), title, onComplete) - - fun showDialog2( - project: Project?, - component: T, - configClass: Class, - title: String = "Generate Project", - onComplete: (C) -> Unit = { _ -> }, - ) = showDialog(project, component, configClass.getConstructor().newInstance(), title, onComplete) - - private fun showDialog( - project: Project?, - uiClass: Class, - config: C, - title: String, - onComplete: (C) -> Unit - ) = showDialog(project, uiClass.getConstructor().newInstance(), config, title, onComplete) - - private fun showDialog( + ): C = showDialog( + project, + uiClass.getConstructor().newInstance(), + configClass.getConstructor().newInstance(), + title, + onComplete + ) + + fun showDialog( project: Project?, component: T, config: C, title: String, onComplete: (C) -> Unit ): C { + log.debug("Showing dialog with title: $title") val dialog = object : DialogWrapper(project) { init { this.init() @@ -635,13 +572,17 @@ object UITools { } override fun createCenterPanel(): JComponent? { + log.debug("Creating center panel for dialog") return buildFormViaReflection(component) } } dialog.show() + log.debug("Dialog shown with result: ${dialog.isOK}") if (dialog.isOK) { readKotlinUIViaReflection(component, config) + log.debug("Reading UI via reflection completed") onComplete(config) + log.debug("onComplete callback executed") } return config } @@ -858,9 +799,11 @@ object UITools { testButton.addActionListener { val apiKey = apiKeyInput.password.joinToString("") try { - OpenAIClient(key = mapOf( - APIProvider.OpenAI to apiKey - )).listModels() + OpenAIClient( + key = mapOf( + APIProvider.OpenAI to apiKey + ) + ).listModels() JOptionPane.showMessageDialog( null, "The API key was accepted by the server. The new value will be saved.", diff --git a/src/test/kotlin/com/github/simiacryptus/aicoder/actions/ActionTestBase.kt b/src/test/kotlin/com/github/simiacryptus/aicoder/actions/ActionTestBase.kt index d4d3764a..580b3e25 100644 --- a/src/test/kotlin/com/github/simiacryptus/aicoder/actions/ActionTestBase.kt +++ b/src/test/kotlin/com/github/simiacryptus/aicoder/actions/ActionTestBase.kt @@ -16,7 +16,7 @@ open class ActionTestBase { fun testScript_SelectionAction(selectionAction: SelectionAction, scriptPath: String) { AppSettingsState.instance.apiKey = ClientUtil.keyMap.mapKeys { it.key }.toMutableMap() AppSettingsState.instance.temperature = 0.0 - AppSettingsState.instance.modelName = ChatModels.GPT35Turbo.name + AppSettingsState.instance.smartModel = ChatModels.GPT35Turbo.name val input = selectionAction.javaClass.getResourceAsStream(scriptPath)?.readAllBytes()?.toString(Charsets.UTF_8) ?: "" @@ -64,7 +64,7 @@ open class ActionTestBase { ) { AppSettingsState.instance.apiKey = ClientUtil.keyMap.mapKeys { it.key }.toMutableMap() AppSettingsState.instance.temperature = 0.0 - AppSettingsState.instance.modelName = ChatModels.GPT35Turbo.name + AppSettingsState.instance.smartModel = ChatModels.GPT35Turbo.name val input = selectionAction.javaClass.getResourceAsStream(scriptPath)?.readAllBytes()?.toString(Charsets.UTF_8) ?: ""