From 80e9a31b05531a4f9e9b822c8414a862828111d8 Mon Sep 17 00:00:00 2001 From: Luo Tim Date: Fri, 20 Sep 2024 12:23:42 +0800 Subject: [PATCH] Refactor LocalService initialization & ensure newest values of devchat service --- .../ai/devchat/core/BaseActionHandler.kt | 7 ++-- .../DeleteLastConversationRequestHandler.kt | 3 +- .../handlers/DeleteTopicRequestHandler.kt | 3 +- .../core/handlers/ListTopicsRequestHandler.kt | 3 +- .../LoadConversationRequestHandler.kt | 5 +-- .../handlers/SendMessageRequestHandler.kt | 14 +++---- .../devchat/installer/DevChatSetupThread.kt | 32 +++++---------- .../plugin/DevChatToolWindowFactory.kt | 40 ++++++++++++++++++- 8 files changed, 64 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt index d2892cc..eabc072 100644 --- a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt +++ b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt @@ -16,9 +16,10 @@ abstract class BaseActionHandler( var payload: JSONObject? = null ) : ActionHandler { val devChatService: DevChatService = project.getService(DevChatService::class.java) - val wrapper: DevChatWrapper? = devChatService.wrapper - val browser: Browser? = devChatService.browser - val activeConversation: ActiveConversation? = devChatService.activeConversation + val client: DevChatClient? get() = devChatService.client + val wrapper: DevChatWrapper? get() = devChatService.wrapper + val browser: Browser? get() = devChatService.browser + val activeConversation: ActiveConversation? get() = devChatService.activeConversation private val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC abstract val actionName: String diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt index fdd3426..d4f1e4a 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class DeleteLastConversationRequestHandler(project: Project, requestAction: Stri override val actionName: String = DevChatActions.DELETE_LAST_CONVERSATION_RESPONSE override fun action() { val promptHash = payload!!.getString("promptHash") - project.getService(DevChatService::class.java).client!!.deleteLog(promptHash) + client!!.deleteLog(promptHash) send(payload = mapOf("promptHash" to promptHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt index 3f1926e..659dafb 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class DeleteTopicRequestHandler(project: Project, requestAction: String, metadat override val actionName: String = DevChatActions.DELETE_TOPIC_RESPONSE override fun action() { val topicHash = payload!!.getString("topicHash") - project.getService(DevChatService::class.java).client!!.deleteTopic(topicHash) + client!!.deleteTopic(topicHash) send(payload = mapOf("topicHash" to topicHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt index c0537af..a514de4 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class ListTopicsRequestHandler(project: Project, requestAction: String, metadata ) { override val actionName: String = DevChatActions.LIST_TOPICS_RESPONSE override fun action() { - val topics = project.getService(DevChatService::class.java).client!!.getTopics().map { + val topics = client!!.getTopics().map { val request = it.rootPromptRequest val response = it.rootPromptResponse mapOf( diff --git a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt index 215756d..0f9fd34 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -21,8 +20,8 @@ class LoadConversationRequestHandler(project: Project, requestAction: String, me topicHash.isNullOrEmpty() -> activeConversation!!.reset() topicHash == activeConversation!!.topic -> res["reset"] = false else -> { - val logs = project.getService(DevChatService::class.java).client!!.getTopicLogs(topicHash) - activeConversation.reset(topicHash, logs) + val logs = client!!.getTopicLogs(topicHash) + activeConversation!!.reset(topicHash, logs) } } send(payload=res) diff --git a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt index 16e902d..5475284 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt @@ -3,7 +3,6 @@ package ai.devchat.core.handlers import ai.devchat.common.Log import ai.devchat.common.PathUtils import ai.devchat.core.* -import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -53,7 +52,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat contextContents = contextContents, ) - project.getService(DevChatService::class.java).client!!.message( + client!!.message( chatRequest, dataHandler(chatRequest), ::errorHandler, @@ -100,11 +99,10 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat } private fun finishHandler(chatRequest: ChatRequest): (Int) -> Unit { val response = chatRequest.response!! - val devChatService = project.getService(DevChatService::class.java) return { exitCode: Int -> when(exitCode) { 0 -> { - val entry = devChatService.client!!.insertLog( + val entry = client!!.insertLog( LogEntry( chatRequest.modelName, chatRequest.parent, @@ -117,12 +115,12 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat promptCallback(response) val currentTopic = activeConversation!!.topic ?: response.promptHash!! - val logs = devChatService.client!!.getTopicLogs(currentTopic, 0, 1) + val logs = client!!.getTopicLogs(currentTopic, 0, 1) - if (currentTopic == activeConversation.topic) { - activeConversation.addMessage(logs.first()) + if (currentTopic == activeConversation!!.topic) { + activeConversation!!.addMessage(logs.first()) } else { - activeConversation.reset(currentTopic, logs) + activeConversation!!.reset(currentTopic, logs) } } -1 -> runWorkflow(chatRequest) diff --git a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt index 58f2e01..4811fc3 100644 --- a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt +++ b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt @@ -4,19 +4,16 @@ import ai.devchat.common.* import ai.devchat.common.Constants.ASSISTANT_NAME_EN import ai.devchat.core.DevChatClient import ai.devchat.plugin.DevChatService -import ai.devchat.plugin.LocalService import ai.devchat.storage.CONFIG import ai.devchat.storage.DevChatState import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer -import com.intellij.ui.content.Content import java.io.BufferedReader import java.io.File import java.nio.file.Paths -class DevChatSetupThread(val project: Project, val toolWindowContent: Content) : Thread() { +class DevChatSetupThread(val project: Project) : Thread() { private val minimalPythonVersion: String = "3.8" private val defaultPythonVersion: String = "3.11.4" private val devChatService = project.getService(DevChatService::class.java) @@ -29,11 +26,11 @@ class DevChatSetupThread(val project: Project, val toolWindowContent: Content) : Notifier.info("Starting $ASSISTANT_NAME_EN initialization...") try { Log.info("Start configuring the $ASSISTANT_NAME_EN CLI environment.") - setup(PythonEnvManager()) - DevChatState.instance.lastVersion = devChatVersion - startLocalService() + setupPython(PythonEnvManager()) + installTools() updateWorkflows() devChatService.browser?.executeJS("onInitializationFinish") + DevChatState.instance.lastVersion = devChatVersion Notifier.info("$ASSISTANT_NAME_EN initialization has completed successfully.") } catch (e: Exception) { Log.error("Failed to install $ASSISTANT_NAME_EN CLI: $e\n" + e.stackTrace.joinToString("\n")) @@ -41,22 +38,10 @@ class DevChatSetupThread(val project: Project, val toolWindowContent: Content) : } } - private fun startLocalService() { - try { - val localService = LocalService(project).start() - devChatService.localServicePort = localService.port!! - devChatService.client = DevChatClient(project, localService.port!!) - Disposer.register(toolWindowContent, localService) - } catch(e: Exception) { - Log.error("Failed to start local service: ${e.message}") - e.printStackTrace() - } - } - - private fun setup(envManager: PythonEnvManager) { + private fun setupPython(envManager: PythonEnvManager) { val overwrite = devChatVersion != DevChatState.instance.lastVersion PathUtils.copyResourceDirToPath("/tools/site-packages", PathUtils.sitePackagePath, overwrite) - "python_for_chat".let{k -> + "python_for_chat".let { k -> if (OSInfo.isWindows) { val installDir = Paths.get(PathUtils.workPath, "python-win").toString() PathUtils.copyResourceDirToPath("/tools/python-3.11.6-embed-amd64", installDir, overwrite) @@ -73,6 +58,11 @@ class DevChatSetupThread(val project: Project, val toolWindowContent: Content) : ).pythonCommand } } + devChatService.pythonReady = true + } + + private fun installTools() { + val overwrite = devChatVersion != DevChatState.instance.lastVersion PathUtils.copyResourceDirToPath( "/tools/code-editor/${PathUtils.codeEditorBinary}", Paths.get(PathUtils.toolsPath, PathUtils.codeEditorBinary).toString(), diff --git a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt index e74166d..48e58b2 100644 --- a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt +++ b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.JBColor import com.intellij.ui.jcef.JBCefApp +import kotlinx.coroutines.* import java.awt.BorderLayout import javax.swing.BorderFactory import javax.swing.JLabel @@ -28,6 +29,7 @@ class DevChatService(project: Project) { var ideServicePort: Int? = null var client: DevChatClient? = null var wrapper: DevChatWrapper? = null + var pythonReady: Boolean = false var uiLoaded: Boolean = false } @@ -36,6 +38,7 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { val devChatService = project.getService(DevChatService::class.java) val browser = Browser(project) devChatService.browser = browser + val panel = JPanel(BorderLayout()) if (!JBCefApp.isSupported()) { Log.error("JCEF is not supported.") @@ -47,9 +50,20 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { val content = toolWindow.contentManager.factory.createContent(panel, "", false) Disposer.register(content, this) - toolWindow.contentManager.addContent(content) - DevChatSetupThread(project, content).start() Disposer.register(content, browser) + + DevChatSetupThread(project).start() + + CoroutineScope(Dispatchers.IO).launch { + withTimeoutOrNull(5000) { + while (!devChatService.pythonReady) { delay(100) } + LocalService(project).start() + }?.let { + Disposer.register(content, it) + devChatService.localServicePort = it.port!! + devChatService.client = DevChatClient(project, it.port!!) + } + } IDEServer(project).start().let { Disposer.register(content, it) devChatService.ideServicePort = it.port @@ -59,7 +73,29 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { devChatService.wrapper = it } devChatService.activeConversation = ActiveConversation() + + toolWindow.contentManager.addContent(content) } +// private fun startLocalService() { +// val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception -> +// Log.error("Failed to start local service: ${exception.message}") +// } +// coroutineScope = CoroutineScope(Dispatchers.Default) +// coroutineScope!!.launch(coroutineExceptionHandler) { +// try { +// while (!pythonReady) { +// delay(100) +// ensureActive() +// } +// localService = LocalService().start() +// awaitCancellation() +// } finally { +// localService?.stop() +// } +// } +// } + + override fun dispose() {} }