Skip to content

Commit 525d697

Browse files
committed
Add BaseActionHandler & refactor all handlers
1 parent c517ab7 commit 525d697

32 files changed

Lines changed: 389 additions & 914 deletions

src/main/kotlin/ai/devchat/cli/DevChatWrapper.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ai.devchat.cli
22

3+
import ai.devchat.common.DevChatPathUtil
34
import ai.devchat.common.Log
5+
import ai.devchat.common.Settings
46
import com.alibaba.fastjson.JSON
57
import com.alibaba.fastjson.JSONArray
68
import com.intellij.util.containers.addIfNotNull
@@ -9,21 +11,19 @@ import java.io.IOException
911

1012
private const val DEFAULT_LOG_MAX_COUNT = 10000
1113

12-
class DevChatWrapper {
13-
private var apiBase: String? = null
14-
private var apiKey: String? = null
14+
class DevChatWrapper(
15+
private val command: String = DevChatPathUtil.devchatBinPath,
16+
private var apiBase: String? = null,
17+
private var apiKey: String? = null,
1518
private var currentModel: String? = null
16-
private var command: String
17-
18-
constructor(command: String) {
19-
this.command = command
20-
}
21-
22-
constructor(apiBase: String?, apiKey: String?, currentModel: String?, command: String) {
23-
this.apiBase = apiBase
24-
this.apiKey = apiKey
25-
this.currentModel = currentModel
26-
this.command = command
19+
) {
20+
init {
21+
if (apiBase.isNullOrEmpty() || apiKey.isNullOrEmpty() || currentModel.isNullOrEmpty()) {
22+
val (key, api, model) = Settings.getAPISettings()
23+
apiBase = apiBase ?: api
24+
apiKey = apiKey ?: key
25+
currentModel = currentModel ?: model
26+
}
2727
}
2828

2929
private fun execCommand(commands: List<String>, callback: ((String) -> Unit)?): String? {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package ai.devchat.common
2+
3+
import ai.devchat.idea.settings.DevChatSettingsState
4+
import ai.devchat.idea.storage.SensitiveDataStorage
5+
6+
object Settings {
7+
fun getAPISettings() : Triple<String?, String?, String?> {
8+
val settings = DevChatSettingsState.instance
9+
val apiKey = SensitiveDataStorage.key
10+
if (settings.apiBase.isEmpty()) {
11+
settings.apiBase = when {
12+
apiKey?.startsWith("sk-") == true -> "https://api.openai.com/v1"
13+
apiKey?.startsWith("DC.") == true -> "https://api.devchat.ai/v1"
14+
else -> settings.apiBase
15+
}
16+
}
17+
return Triple(apiKey, settings.apiBase, settings.defaultModel)
18+
}
19+
}

src/main/kotlin/ai/devchat/devchat/ActionHandler.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,5 @@ package ai.devchat.devchat
33
import com.alibaba.fastjson.JSONObject
44

55
interface ActionHandler {
6-
fun setMetadata(metadata: JSONObject)
7-
fun setPayload(payload: JSONObject)
86
fun executeAction()
97
}
Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,41 @@
11
package ai.devchat.devchat
22

33
import ai.devchat.devchat.handler.*
4-
import java.lang.reflect.InvocationTargetException
4+
import com.alibaba.fastjson.JSONObject
55
import kotlin.reflect.KClass
66
import kotlin.reflect.full.primaryConstructor
77

88
class ActionHandlerFactory {
9-
private val actionHandlerMap: Map<String, KClass<out ActionHandler>> =
10-
object : HashMap<String, KClass<out ActionHandler>>() {
11-
init {
12-
put(DevChatActions.SEND_MESSAGE_REQUEST, SendMessageRequestHandler::class)
13-
put(DevChatActions.SET_OR_UPDATE_KEY_REQUEST, SetOrUpdateKeyRequestHandler::class)
14-
put(DevChatActions.LIST_COMMANDS_REQUEST, ListCommandsRequestHandler::class)
15-
put(DevChatActions.LOAD_CONVERSATIONS_REQUEST, LoadConversationRequestHandler::class)
16-
put(DevChatActions.LOAD_HISTORY_MESSAGES_REQUEST, LoadHistoryMessagesRequestHandler::class)
17-
put(DevChatActions.OPEN_LINK_REQUEST, OpenLinkRequestHandler::class)
18-
put(DevChatActions.LIST_TOPICS_REQUEST, ListTopicsRequestHandler::class)
19-
put(DevChatActions.INSERT_CODE_REQUEST, InsertCodeRequestHandler::class)
20-
put(DevChatActions.REPLACE_FILE_CONTENT_REQUEST, ReplaceFileContentHandler::class)
21-
put(DevChatActions.VIEW_DIFF_REQUEST, ViewDiffRequestHandler::class)
22-
put(DevChatActions.LIST_CONTEXTS_REQUEST, ListContextsRequestHandler::class)
23-
put(DevChatActions.LIST_MODELS_REQUEST, ListModelsRequestHandler::class)
24-
put(DevChatActions.ADD_CONTEXT_REQUEST, AddContextRequestHandler::class)
25-
put(DevChatActions.GET_KEY_REQUEST, GetKeyRequestHandler::class)
26-
put(DevChatActions.COMMIT_CODE_REQUEST, CommitCodeRequestHandler::class)
27-
put(DevChatActions.GET_SETTING_REQUEST, GetSettingRequestHandler::class)
28-
put(DevChatActions.UPDATE_SETTING_REQUEST, UpdateSettingRequestHandler::class)
29-
put(DevChatActions.SHOW_SETTING_DIALOG_REQUEST, ShowSettingDialogRequestHandler::class)
30-
put(DevChatActions.DELETE_LAST_CONVERSATION_REQUEST, DeleteLastConversationRequestHandler::class)
31-
put(DevChatActions.DELETE_TOPIC_REQUEST, DeleteTopicRequestHandler::class)
32-
}
33-
}
9+
private val actionHandlerMap: Map<String, KClass<out ActionHandler>> = mapOf(
10+
DevChatActions.SEND_MESSAGE_REQUEST to SendMessageRequestHandler::class,
11+
DevChatActions.SET_OR_UPDATE_KEY_REQUEST to SetOrUpdateKeyRequestHandler::class,
12+
DevChatActions.LIST_COMMANDS_REQUEST to ListCommandsRequestHandler::class,
13+
DevChatActions.LOAD_CONVERSATIONS_REQUEST to LoadConversationRequestHandler::class,
14+
DevChatActions.LOAD_HISTORY_MESSAGES_REQUEST to LoadHistoryMessagesRequestHandler::class,
15+
DevChatActions.OPEN_LINK_REQUEST to OpenLinkRequestHandler::class,
16+
DevChatActions.LIST_TOPICS_REQUEST to ListTopicsRequestHandler::class,
17+
DevChatActions.INSERT_CODE_REQUEST to InsertCodeRequestHandler::class,
18+
DevChatActions.REPLACE_FILE_CONTENT_REQUEST to ReplaceFileContentHandler::class,
19+
DevChatActions.VIEW_DIFF_REQUEST to ViewDiffRequestHandler::class,
20+
DevChatActions.LIST_CONTEXTS_REQUEST to ListContextsRequestHandler::class,
21+
DevChatActions.LIST_MODELS_REQUEST to ListModelsRequestHandler::class,
22+
DevChatActions.ADD_CONTEXT_REQUEST to AddContextRequestHandler::class,
23+
DevChatActions.GET_KEY_REQUEST to GetKeyRequestHandler::class,
24+
DevChatActions.COMMIT_CODE_REQUEST to CommitCodeRequestHandler::class,
25+
DevChatActions.GET_SETTING_REQUEST to GetSettingRequestHandler::class,
26+
DevChatActions.UPDATE_SETTING_REQUEST to UpdateSettingRequestHandler::class,
27+
DevChatActions.SHOW_SETTING_DIALOG_REQUEST to ShowSettingDialogRequestHandler::class,
28+
DevChatActions.DELETE_LAST_CONVERSATION_REQUEST to DeleteLastConversationRequestHandler::class,
29+
DevChatActions.DELETE_TOPIC_REQUEST to DeleteTopicRequestHandler::class,
30+
)
3431

35-
fun createActionHandler(action: String): ActionHandler {
36-
val handlerClass = actionHandlerMap[action]
37-
return if (handlerClass != null) {
38-
try {
39-
handlerClass.primaryConstructor!!.call(DevChatActionHandler.instance)
40-
} catch (e: NoSuchMethodException) {
41-
throw RuntimeException("Failed to instantiate action handler for: $action", e)
42-
} catch (e: InstantiationException) {
43-
throw RuntimeException("Failed to instantiate action handler for: $action", e)
44-
} catch (e: IllegalAccessException) {
45-
throw RuntimeException("Failed to instantiate action handler for: $action", e)
46-
} catch (e: InvocationTargetException) {
47-
throw RuntimeException("Failed to instantiate action handler for: $action", e)
48-
}
49-
} else {
50-
throw RuntimeException("Action handler not found: $action")
32+
fun createActionHandler(action: String, metadata: JSONObject, payload: JSONObject): ActionHandler {
33+
val handlerClass = actionHandlerMap[action] ?: throw RuntimeException("Action handler not found: $action")
34+
return try {
35+
handlerClass.primaryConstructor!!.call(metadata, payload)
36+
} catch (e: Exception) {
37+
// Catch any exception since the handling is the same
38+
throw RuntimeException("Failed to instantiate action handler for: $action", e)
5139
}
5240
}
5341
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package ai.devchat.devchat
2+
3+
import ai.devchat.cli.DevChatWrapper
4+
import ai.devchat.common.DevChatPathUtil
5+
import ai.devchat.common.Log
6+
import com.alibaba.fastjson.JSONObject
7+
8+
const val DEFAULT_RESPONSE_FUNC = "IdeaToJSMessage"
9+
10+
abstract class BaseActionHandler(
11+
val metadata: JSONObject? = null,
12+
val payload: JSONObject? = null
13+
) : ActionHandler {
14+
val handler = DevChatActionHandler.instance
15+
val wrapper = DevChatWrapper()
16+
val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC
17+
18+
abstract val actionName: String
19+
20+
open fun action() { response() }
21+
22+
open fun except(exception: Exception) {
23+
response(
24+
metadata = mapOf(
25+
"status" to "error",
26+
"error" to exception
27+
)
28+
)
29+
}
30+
31+
fun response(metadata: Map<String, Any?>? = null, payload: Map<String, Any?>? = null) {
32+
handler?.sendResponse(
33+
actionName,
34+
jsCallback,
35+
metadata?.let { JSONObject(it) },
36+
payload?.let { JSONObject(it) },
37+
)
38+
}
39+
40+
override fun executeAction() {
41+
try {
42+
Log.info("Handling $actionName request")
43+
action()
44+
} catch (e: Exception) {
45+
e.printStackTrace()
46+
Log.error("Exception occurred while handle action $actionName: ${e.message}")
47+
except(e)
48+
}
49+
}
50+
51+
}

src/main/kotlin/ai/devchat/devchat/DevChatActionHandler.kt

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import org.cef.browser.CefBrowser
1111
* DevChatActionHandler class uses singleton pattern.
1212
*/
1313
class DevChatActionHandler private constructor() {
14-
val devChat: DevChatWrapper = DevChatWrapper(DevChatPathUtil.devchatBinPath)
1514
private var cefBrowser: CefBrowser? = null
1615
var project: Project? = null
1716
private set
@@ -47,15 +46,20 @@ class DevChatActionHandler private constructor() {
4746
cefBrowser!!.executeJavaScript("$jsCallback($response)", "", 0)
4847
}
4948

50-
fun sendResponse(action: String, responseFunc: String, callback: (JSONObject, JSONObject) -> Unit) {
49+
fun sendResponse(
50+
action: String,
51+
jsCallback: String,
52+
metadata: JSONObject? = null,
53+
payload: JSONObject? = null,
54+
) {
5155
val response = JSONObject()
5256
response["action"] = action
53-
val metadata = JSONObject()
54-
val payload = JSONObject()
55-
response["metadata"] = metadata
56-
response["payload"] = payload
57-
callback(metadata, payload)
58-
cefBrowser!!.executeJavaScript("$responseFunc($response)", "", 0)
57+
response["metadata"] = metadata ?: JSONObject(mapOf(
58+
"status" to "success",
59+
"error" to ""
60+
))
61+
response["payload"] = payload ?: JSONObject()
62+
cefBrowser!!.executeJavaScript("$jsCallback($response)", "", 0)
5963
}
6064

6165
companion object {
Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,17 @@
11
package ai.devchat.devchat.handler
22

3-
import ai.devchat.devchat.ActionHandler
4-
import ai.devchat.devchat.DevChatActionHandler
5-
import ai.devchat.devchat.DevChatActions
3+
import ai.devchat.devchat.*
64
import com.alibaba.fastjson.JSONObject
75

8-
class AddContextNotifyHandler(private val devChatActionHandler: DevChatActionHandler) : ActionHandler {
9-
private var metadata: JSONObject? = null
10-
private var payload: JSONObject? = null
11-
val RESPONSE_FUNC = "IdeaToJSMessage"
12-
override fun executeAction() {
13-
devChatActionHandler.sendResponse(
14-
DevChatActions.ADD_CONTEXT_NOTIFY,
15-
RESPONSE_FUNC
16-
) { metadata: JSONObject, payload: JSONObject ->
17-
metadata["status"] = "success"
18-
metadata["error"] = ""
19-
payload["path"] = this.payload!!.getString("path")
20-
payload["content"] = this.payload!!.getString("content")
21-
payload["languageId"] = this.payload!!.getString("languageId")
22-
payload["startLine"] = this.payload!!.getInteger("startLine")
23-
}
24-
}
25-
26-
override fun setMetadata(metadata: JSONObject) {
27-
this.metadata = metadata
28-
}
296

30-
override fun setPayload(payload: JSONObject) {
31-
this.payload = payload
7+
class AddContextNotifyHandler(metadata: JSONObject?, payload: JSONObject?) : BaseActionHandler(metadata, payload) {
8+
override val actionName: String = DevChatActions.ADD_CONTEXT_NOTIFY
9+
override fun action() {
10+
response(payload=mapOf(
11+
"path" to payload?.getString("path"),
12+
"content" to payload?.getString("content"),
13+
"languageId" to payload?.getString("languageId"),
14+
"startLine" to payload?.getInteger("startLine")
15+
))
3216
}
3317
}
Lines changed: 20 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,32 @@
11
package ai.devchat.devchat.handler
22

3-
import ai.devchat.common.Log
4-
import ai.devchat.devchat.ActionHandler
5-
import ai.devchat.devchat.DevChatActionHandler
3+
import ai.devchat.devchat.BaseActionHandler
64
import ai.devchat.devchat.DevChatActions
75
import com.alibaba.fastjson.JSONObject
86
import java.io.BufferedReader
97
import java.io.File
10-
import java.io.IOException
11-
import java.io.InputStreamReader
128

13-
class AddContextRequestHandler(private val devChatActionHandler: DevChatActionHandler) : ActionHandler {
14-
private var metadata: JSONObject? = null
15-
private var payload: JSONObject? = null
16-
override fun executeAction() {
17-
Log.info("Handling add context request")
18-
val command = payload!!.getString("command")
19-
val result = StringBuilder()
20-
val error = StringBuilder()
21-
var reader: BufferedReader? = null
22-
var errorReader: BufferedReader? = null
23-
try {
24-
val projectDir = devChatActionHandler.project?.basePath
25-
val process = Runtime.getRuntime().exec(command, null, projectDir?.let { File(it) })
26-
reader = BufferedReader(InputStreamReader(process.inputStream))
27-
var line: String?
28-
while (reader.readLine().also { line = it } != null) {
29-
result.append(line).append("\n")
30-
}
31-
errorReader = BufferedReader(InputStreamReader(process.errorStream))
32-
while (errorReader.readLine().also { line = it } != null) {
33-
error.append(line).append("\n")
34-
}
35-
} catch (e: IOException) {
36-
error.append(e.message)
37-
} finally {
38-
if (reader != null) {
39-
try {
40-
reader.close()
41-
} catch (e: IOException) {
42-
e.printStackTrace()
43-
}
44-
}
45-
if (errorReader != null) {
46-
try {
47-
errorReader.close()
48-
} catch (e: IOException) {
49-
e.printStackTrace()
50-
}
51-
}
52-
}
53-
val callbackFunc = metadata!!.getString("callback")
54-
if (error.isEmpty()) {
55-
val finalResult = result.toString()
56-
devChatActionHandler.sendResponse(
57-
DevChatActions.ADD_CONTEXT_RESPONSE,
58-
callbackFunc
59-
) { metadata: JSONObject, payload: JSONObject ->
60-
metadata["status"] = "success"
61-
metadata["error"] = ""
62-
payload["command"] = command
63-
payload["content"] = finalResult
64-
}
65-
} else {
66-
val finalError = error.toString()
67-
devChatActionHandler.sendResponse(
68-
DevChatActions.ADD_CONTEXT_RESPONSE,
69-
callbackFunc
70-
) { metadata: JSONObject, payload: JSONObject ->
71-
metadata["status"] = "error"
72-
metadata["error"] = finalError
73-
payload["command"] = command
74-
payload["content"] = ""
75-
}
76-
}
77-
}
9+
class AddContextRequestHandler(metadata: JSONObject?, payload: JSONObject?) : BaseActionHandler(metadata, payload) {
10+
override val actionName: String = DevChatActions.ADD_CONTEXT_RESPONSE
11+
val command: String? = payload?.getString("command")
7812

79-
override fun setMetadata(metadata: JSONObject) {
80-
this.metadata = metadata
13+
override fun action() {
14+
val projectDir = handler?.project?.basePath
15+
val process = Runtime.getRuntime().exec(command, null, projectDir?.let { File(it) })
16+
val result = process.inputStream.bufferedReader().use(BufferedReader::readText)
17+
val errors = process.errorStream.bufferedReader().use(BufferedReader::readText)
18+
process.waitFor()
19+
val exitCode = process.exitValue()
20+
if (exitCode != 0) {
21+
throw RuntimeException("Failed to execute command: $command, Exit Code: $exitCode Error: $errors")
22+
}
23+
response(payload=mapOf("command" to command, "content" to result))
8124
}
8225

83-
override fun setPayload(payload: JSONObject) {
84-
this.payload = payload
26+
override fun except(exception: Exception) {
27+
response(
28+
metadata=mapOf("status" to "error", "error" to exception.message),
29+
payload=mapOf("command" to command, "content" to "")
30+
)
8531
}
8632
}

0 commit comments

Comments
 (0)