Skip to content

Commit

Permalink
Merge pull request #209 from devchat-ai/support-multiple-projects
Browse files Browse the repository at this point in the history
Support multiple projects
  • Loading branch information
pplam authored Sep 19, 2024
2 parents b526d4c + f00d7af commit db427d2
Show file tree
Hide file tree
Showing 34 changed files with 176 additions and 178 deletions.
2 changes: 0 additions & 2 deletions src/main/kotlin/ai/devchat/common/PathUtils.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package ai.devchat.common

import ai.devchat.plugin.currentProject
import java.io.File
import java.io.IOException
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes


object PathUtils {
val workspace: String? = currentProject?.basePath
val workPath: String = Paths.get(System.getProperty("user.home"), ".chat").toString()
val workflowPath: String = Paths.get(workPath, "scripts").toString()
val sitePackagePath: String = Paths.get(workPath, "site-packages").toString()
Expand Down
16 changes: 10 additions & 6 deletions src/main/kotlin/ai/devchat/core/BaseActionHandler.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ai.devchat.core

import ai.devchat.common.Log
import ai.devchat.plugin.DevChatBrowserService
import ai.devchat.plugin.Browser
import ai.devchat.plugin.DevChatService
import ai.devchat.storage.ActiveConversation
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project

Expand All @@ -13,8 +15,12 @@ abstract class BaseActionHandler(
var metadata: JSONObject? = null,
var payload: JSONObject? = null
) : ActionHandler {
val wrapper = DevChatWrapper()
val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC
private val devChatService: DevChatService = project.getService(DevChatService::class.java)
val client: DevChatClient? = devChatService.client
val wrapper: DevChatWrapper? = devChatService.wrapper
val browser: Browser? = devChatService.browser
val activeConversation: ActiveConversation? = devChatService.activeConversation
private val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC

abstract val actionName: String

Expand All @@ -37,9 +43,7 @@ abstract class BaseActionHandler(
"error" to ""
))
response["payload"] = payload ?: JSONObject()
val browser = project.getService(DevChatBrowserService::class.java).browser?.let {
it.executeJS(jsCallback, response)
}
browser!!.executeJS(jsCallback, response)
}

override fun executeAction() {
Expand Down
25 changes: 11 additions & 14 deletions src/main/kotlin/ai/devchat/core/DevChatClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package ai.devchat.core

import ai.devchat.common.Log
import ai.devchat.common.PathUtils
import ai.devchat.plugin.localServicePort
import com.intellij.openapi.project.Project
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
Expand All @@ -27,7 +27,6 @@ import java.time.Instant
import kotlin.system.measureTimeMillis



inline fun <reified T> T.asMap(): Map<String, Any?> where T : @Serializable Any {
val json = Json { encodeDefaults = true }
val jsonString = json.encodeToString(serializer(),this)
Expand All @@ -42,7 +41,7 @@ data class ChatRequest(
@SerialName("api_base") val apiBase: String,
val parent: String?,
val context: List<String>?,
val workspace: String? = PathUtils.workspace,
val workspace: String? = null,
@Transient val contextContents: List<String>? = null,
@Transient val response: ChatResponse? = null,
)
Expand Down Expand Up @@ -209,10 +208,10 @@ fun timeThis(block: suspend () -> Unit) {
}
}

class DevChatClient {
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
class DevChatClient(val project: Project, private val localServicePort: Int) {
private val baseURL get() = "http://localhost:$localServicePort"
private var job: Job? = null
private val workspace: String? = project.basePath

companion object {
const val DEFAULT_LOG_MAX_COUNT = 10000
Expand Down Expand Up @@ -315,7 +314,7 @@ class DevChatClient {
onFinish: (Int) -> Unit,
) {
cancelMessage()
job = scope.launch {
job = CoroutineScope(Dispatchers.IO).launch {
streamPost<ChatRequest, ChatResponse>("/message/msg", message)
.catch { e ->
onError(e.toString())
Expand Down Expand Up @@ -350,7 +349,7 @@ class DevChatClient {
}

fun insertLog(logEntry: LogEntry): LogInsertRes? {
val body = mutableMapOf("workspace" to PathUtils.workspace)
val body = mutableMapOf("workspace" to workspace)
val jsonData = json.encodeToString(serializer(), logEntry)
if (jsonData.length <= LOG_RAW_DATA_SIZE_LIMIT) {
body["jsondata"] = jsonData
Expand All @@ -369,30 +368,30 @@ class DevChatClient {
}
fun deleteLog(logHash: String): LogDeleteRes? {
return post("/logs/delete", mapOf(
"workspace" to PathUtils.workspace,
"workspace" to workspace,
"hash" to logHash
))
}
fun getTopicLogs(topicRootHash: String, offset: Int = 0, limit: Int = DEFAULT_LOG_MAX_COUNT): List<ShortLog> {
return get<List<ShortLog>>("/topics/$topicRootHash/logs", mapOf(
"limit" to limit,
"offset" to offset,
"workspace" to PathUtils.workspace,
"workspace" to workspace,
)) ?: emptyList()
}
fun getTopics(offset: Int = 0, limit: Int = DEFAULT_LOG_MAX_COUNT): List<Topic> {
val queryParams = mapOf(
"limit" to limit,
"offset" to offset,
"workspace" to PathUtils.workspace,
"workspace" to workspace,
)
return get<List<Topic>?>("/topics", queryParams).orEmpty()
}

fun deleteTopic(topicRootHash: String) {
val response: Map<String, String>? = post("/topics/delete", mapOf(
"topic_hash" to topicRootHash,
"workspace" to PathUtils.workspace,
"workspace" to workspace,
))
Log.info("deleteTopic response data: $response")
}
Expand All @@ -401,6 +400,4 @@ class DevChatClient {
job?.cancel()
job = null
}
}

val DC_CLIENT: DevChatClient = DevChatClient()
}
28 changes: 14 additions & 14 deletions src/main/kotlin/ai/devchat/core/DevChatWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package ai.devchat.core
import ai.devchat.common.Log
import ai.devchat.common.Notifier
import ai.devchat.common.PathUtils
import ai.devchat.plugin.currentProject
import ai.devchat.plugin.ideServerPort
import ai.devchat.storage.CONFIG
import com.intellij.execution.process.OSProcessUtil.killProcessTree
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import com.intellij.util.containers.addIfNotNull
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.SendChannel
Expand Down Expand Up @@ -46,10 +47,10 @@ suspend fun executeCommand(
}
}

class Command(val cmd: MutableList<String> = mutableListOf()) {
class Command(private val workDir: String?, val cmd: MutableList<String> = mutableListOf()) {
private val env: MutableMap<String, String> = mutableMapOf()

constructor(parent: Command) : this() {
constructor(parent: Command) : this(parent.workDir) {
cmd.addAll(parent.cmd)
env.putAll(parent.env)
}
Expand Down Expand Up @@ -92,11 +93,7 @@ class Command(val cmd: MutableList<String> = mutableListOf()) {
val outputLines: MutableList<String> = mutableListOf()
val errorLines: MutableList<String> = mutableListOf()
val exitCode = runBlocking {
executeCommand(
preparedCommand,
currentProject?.basePath,
env
).await(outputLines::add, errorLines::add)
executeCommand(preparedCommand, workDir, env).await(outputLines::add, errorLines::add)
}
val errors = errorLines.joinToString("\n")
val outputs = outputLines.joinToString("\n")
Expand All @@ -105,7 +102,9 @@ class Command(val cmd: MutableList<String> = mutableListOf()) {
Log.info("Execution time: ${endTime - startTime} ms")

if (exitCode != 0) {
throw CommandExecutionException("Command failure with exit Code: $exitCode, Errors: $errors, Outputs: $outputs")
throw CommandExecutionException(
"Command failure with exit Code: $exitCode, Errors: $errors, Outputs: $outputs"
)
} else {
outputs
}
Expand Down Expand Up @@ -138,7 +137,7 @@ class Command(val cmd: MutableList<String> = mutableListOf()) {
return CoroutineScope(
SupervisorJob() + Dispatchers.Default + exceptionHandler
).actor {
val process = executeCommand(preparedCommand, currentProject?.basePath, env)
val process = executeCommand(preparedCommand, workDir, env)
val writer = process.outputStream.bufferedWriter()
val errorLines: MutableList<String> = mutableListOf()
val deferred = async {process.await(onOutput, errorLines::add)}
Expand Down Expand Up @@ -183,10 +182,10 @@ class Command(val cmd: MutableList<String> = mutableListOf()) {
}
}

class DevChatWrapper(
) {
class DevChatWrapper(val project: Project): Disposable {
private val apiKey get() = CONFIG["providers.devchat.api_key"] as? String
private val defaultModel get() = CONFIG["default_model"] as? String
var activeChannel: SendChannel<String>? = null

private val apiBase: String?
get() {
Expand All @@ -203,6 +202,7 @@ class DevChatWrapper(
return v
}
private val baseCommand get() = Command(
project.basePath,
mutableListOf(CONFIG["python_for_chat"] as String, "-m", "devchat")
).addEnv(getEnv())

Expand Down Expand Up @@ -244,8 +244,8 @@ class DevChatWrapper(
activeChannel = routeCmd(flags + additionalFlags, callback, onError, onFinish)
}

companion object {
var activeChannel: SendChannel<String>? = null
override fun dispose() {
activeChannel?.close()
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DC_CLIENT
import ai.devchat.core.DevChatActions
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand All @@ -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")
DC_CLIENT.deleteLog(promptHash)
client!!.deleteLog(promptHash)
send(payload = mapOf("promptHash" to promptHash))
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DC_CLIENT
import ai.devchat.core.DevChatActions
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand All @@ -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")
DC_CLIENT.deleteTopic(topicHash)
client!!.deleteTopic(topicHash)
send(payload = mapOf("topicHash" to topicHash))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DevChatActions
import ai.devchat.core.DevChatWrapper
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
import kotlinx.coroutines.runBlocking
Expand All @@ -17,7 +16,7 @@ class InputRequestHandler(project: Project, requestAction: String, metadata: JSO

override fun action() {
runBlocking {
DevChatWrapper.activeChannel?.send(payload!!.getString("data"))
wrapper!!.activeChannel?.send(payload!!.getString("data"))
}
send()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DevChatActions
import ai.devchat.plugin.currentProject
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
Expand All @@ -19,10 +18,10 @@ class InsertCodeRequestHandler(project: Project, requestAction: String, metadata
override fun action() {
val contentText = payload!!.getString("content")
ApplicationManager.getApplication().invokeLater {
val editor = currentProject?.let { FileEditorManager.getInstance(it).selectedTextEditor }
val editor = FileEditorManager.getInstance(project).selectedTextEditor
val document = editor!!.document
val offset = editor.caretModel.offset
CommandProcessor.getInstance().executeCommand(currentProject, {
CommandProcessor.getInstance().executeCommand(project, {
ApplicationManager.getApplication().runWriteAction {
document.insertString(offset, contentText)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DC_CLIENT
import ai.devchat.core.DevChatActions
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand All @@ -15,8 +14,8 @@ class ListCommandsRequestHandler(project: Project, requestAction: String, metada
) {
override val actionName: String = DevChatActions.LIST_COMMANDS_RESPONSE
override fun action() {
val recommendedWorkflows = DC_CLIENT.getWorkflowConfig()?.recommend?.workflows.orEmpty()
val indexedCommands = DC_CLIENT.getWorkflowList()?.map {
val recommendedWorkflows = client!!.getWorkflowConfig()?.recommend?.workflows.orEmpty()
val indexedCommands = client.getWorkflowList()?.map {
val commandName = it.name
mapOf(
"name" to it.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DC_CLIENT
import ai.devchat.core.DevChatActions
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand All @@ -15,7 +14,7 @@ class ListTopicsRequestHandler(project: Project, requestAction: String, metadata
) {
override val actionName: String = DevChatActions.LIST_TOPICS_RESPONSE
override fun action() {
val topics = DC_CLIENT.getTopics().map {
val topics = client!!.getTopics().map {
val request = it.rootPromptRequest
val response = it.rootPromptResponse
mapOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DC_CLIENT
import ai.devchat.core.DevChatActions
import ai.devchat.storage.ActiveConversation
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project

Expand All @@ -19,11 +17,11 @@ class LoadConversationRequestHandler(project: Project, requestAction: String, me
val topicHash = metadata!!.getString("topicHash")
val res = mutableMapOf("reset" to true)
when {
topicHash.isNullOrEmpty() -> ActiveConversation.reset()
topicHash == ActiveConversation.topic -> res["reset"] = false
topicHash.isNullOrEmpty() -> activeConversation!!.reset()
topicHash == activeConversation!!.topic -> res["reset"] = false
else -> {
val logs = DC_CLIENT.getTopicLogs(topicHash)
ActiveConversation.reset(topicHash, logs)
val logs = client!!.getTopicLogs(topicHash)
activeConversation.reset(topicHash, logs)
}
}
send(payload=res)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ai.devchat.core.handlers

import ai.devchat.core.BaseActionHandler
import ai.devchat.core.DevChatActions
import ai.devchat.storage.ActiveConversation
import ai.devchat.storage.CONFIG
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand All @@ -18,7 +17,7 @@ class LoadHistoryMessagesRequestHandler(project: Project, requestAction: String,
override fun action() {
val pageSize = CONFIG["max_log_count"] as Int
val pageIndex = metadata!!.getInteger("pageIndex") ?: 1
val messages = ActiveConversation.getMessages(pageIndex, pageSize)
val messages = activeConversation!!.getMessages(pageIndex, pageSize)
send(payload = mapOf("messages" to messages))
}
}
Loading

0 comments on commit db427d2

Please sign in to comment.