Skip to content

Commit 19939f6

Browse files
authored
Merge pull request #21 from devchat-ai/conversation-history
Refactor & bug fix
2 parents 8b559db + 2dc6a78 commit 19939f6

38 files changed

Lines changed: 455 additions & 942 deletions

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

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,23 @@
11
package ai.devchat.cli
22

3-
/*
4-
* User: Daniel Hu <tao.hu@merico.dev>
5-
* Date: Mon Oct 16 22:40:06 2023 +0800
6-
*
7-
* Hello! How can I assist you today?
8-
*
9-
* prompt 6e2a0d9b5c15eb33008250fee40383e77e8f80c75d9644b15bda60be256c8010
10-
*/
11-
class DevChatResponse(line: String) {
3+
class DevChatResponse {
124
var user: String? = null
135
var date: String? = null
146
var message: String? = null
157
var promptHash: String? = null
168

17-
init {
18-
if (line.startsWith("User: ") && user == null) {
19-
user = line.substring("User: ".length)
20-
} else if (line.startsWith("Date: ") && date == null) {
21-
date = line.substring("Date: ".length)
22-
// 71 is the length of string
23-
// "prompt 6e2a0d9b5c15eb33008250fee40383e77e8f80c75d9644b15bda60be256c8010"
24-
} else if (line.startsWith("prompt ") && line.length == 71) {
25-
promptHash = line.substring("prompt ".length)
26-
message += "\n"
27-
} else if (!line.isEmpty()) {
28-
if (message == null) {
29-
message = line
30-
} else {
31-
message += """
32-
33-
$line
34-
""".trimIndent()
9+
fun update(line: String) : DevChatResponse {
10+
when {
11+
line.startsWith("User: ") -> user = user ?: line.substring("User: ".length)
12+
line.startsWith("Date: ") -> date = date ?: line.substring("Date: ".length)
13+
// 71 is the length of the prompt hash
14+
line.startsWith("prompt ") && line.length == 71 -> {
15+
promptHash = line.substring("prompt ".length)
16+
message = message?.let { "$it\n" } ?: "\n"
3517
}
18+
line.isNotEmpty() -> message = message?.let { "$it\n$line" } ?: line
3619
}
20+
return this
3721
}
3822

3923
override fun toString(): String {

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

Lines changed: 15 additions & 15 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? {
@@ -44,7 +44,7 @@ class DevChatWrapper {
4444
val text = process.inputStream.bufferedReader().use { reader ->
4545
callback?.let {
4646
reader.forEachLine(it)
47-
null
47+
""
4848
} ?: reader.readText()
4949
}
5050
val errors = process.errorStream.bufferedReader().use(BufferedReader::readText)
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 & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +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.LIST_TOPICS_REQUEST, ListTopicsRequestHandler::class)
18-
put(DevChatActions.INSERT_CODE_REQUEST, InsertCodeRequestHandler::class)
19-
put(DevChatActions.REPLACE_FILE_CONTENT_REQUEST, ReplaceFileContentHandler::class)
20-
put(DevChatActions.VIEW_DIFF_REQUEST, ViewDiffRequestHandler::class)
21-
put(DevChatActions.LIST_CONTEXTS_REQUEST, ListContextsRequestHandler::class)
22-
put(DevChatActions.LIST_MODELS_REQUEST, ListModelsRequestHandler::class)
23-
put(DevChatActions.ADD_CONTEXT_REQUEST, AddContextRequestHandler::class)
24-
put(DevChatActions.GET_KEY_REQUEST, GetKeyRequestHandler::class)
25-
put(DevChatActions.COMMIT_CODE_REQUEST, CommitCodeRequestHandler::class)
26-
put(DevChatActions.GET_SETTING_REQUEST, GetSettingRequestHandler::class)
27-
put(DevChatActions.UPDATE_SETTING_REQUEST, UpdateSettingRequestHandler::class)
28-
put(DevChatActions.SHOW_SETTING_DIALOG_REQUEST, ShowSettingDialogRequestHandler::class)
29-
put(DevChatActions.DELETE_LAST_CONVERSATION_REQUEST, DeleteLastConversationRequestHandler::class)
30-
put(DevChatActions.DELETE_TOPIC_REQUEST, DeleteTopicRequestHandler::class)
31-
}
32-
}
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+
)
3331

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

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

Lines changed: 16 additions & 10 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
@@ -23,7 +22,8 @@ class DevChatActionHandler private constructor() {
2322
fun handle(
2423
action: String,
2524
jsCallback: String,
26-
callback: (JSONObject) -> Unit,
25+
success: (JSONObject, JSONObject) -> Unit,
26+
fail: (JSONObject, JSONObject) -> Unit,
2727
) {
2828
val response = JSONObject()
2929
response["action"] = action
@@ -34,26 +34,32 @@ class DevChatActionHandler private constructor() {
3434

3535
try {
3636
Log.info("Handling $action request")
37-
callback(payload)
3837
metadata["status"] = "success"
3938
metadata["error"] = ""
39+
success(metadata, payload)
4040
} catch (e: Exception) {
4141
Log.error("Exception occurred while handle action $action: ${e.message}")
4242
metadata["status"] = "error"
4343
metadata["error"] = e.message
44+
fail(metadata, payload)
4445
}
4546
cefBrowser!!.executeJavaScript("$jsCallback($response)", "", 0)
4647
}
4748

48-
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+
) {
4955
val response = JSONObject()
5056
response["action"] = action
51-
val metadata = JSONObject()
52-
val payload = JSONObject()
53-
response["metadata"] = metadata
54-
response["payload"] = payload
55-
callback(metadata, payload)
56-
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)
5763
}
5864

5965
companion object {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ object DevChatActions {
1414
const val LOAD_CONVERSATIONS_RESPONSE = "loadConversations/response"
1515
const val LOAD_HISTORY_MESSAGES_REQUEST = "loadHistoryMessages/request"
1616
const val LOAD_HISTORY_MESSAGES_RESPONSE = "loadHistoryMessages/response"
17+
const val OPEN_LINK_REQUEST = "openLink/request"
18+
const val OPEN_LINK_RESPONSE = "openLink/response"
1719
const val LIST_TOPICS_REQUEST = "listTopics/request"
1820
const val LIST_TOPICS_RESPONSE = "listTopics/response"
1921
const val INSERT_CODE_REQUEST = "insertCode/request"
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+
send(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
}

0 commit comments

Comments
 (0)