Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 0.3.1 #191

Merged
merged 3 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 60 additions & 30 deletions src/main/kotlin/ai/devchat/core/DevChatClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import java.nio.file.Paths
import java.time.Instant
import kotlin.system.measureTimeMillis

private const val DEFAULT_LOG_MAX_COUNT = 10000


inline fun <reified T> T.asMap(): Map<String, Any?> where T : @Serializable Any {
Expand Down Expand Up @@ -195,6 +194,12 @@ data class WorkflowConfig(
data class Recommend(val workflows: List<String>)
}

@Serializable
data class UpdateWorkflowResponse(
val updated: Boolean,
val message: String? = null,
)

fun timeThis(block: suspend () -> Unit) {
runBlocking {
val time = measureTimeMillis {
Expand All @@ -204,58 +209,83 @@ fun timeThis(block: suspend () -> Unit) {
}
}

class DevChatClient() {
class DevChatClient {
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
private val baseURL get() = "http://localhost:$localServicePort"
private var job: Job? = null

companion object {
const val LOG_RAW_DATA_SIZE_LIMIT = 4 * 1024
const val DEFAULT_LOG_MAX_COUNT = 10000
const val LOG_RAW_DATA_SIZE_LIMIT = 4 * 1024 // 4kb
const val RETRY_INTERVAL: Long = 500 // ms
const val MAX_RETRIES: Int = 10
}
private val client = OkHttpClient()
private val json = Json { ignoreUnknownKeys = true }

private inline fun <reified T> get(
path: String,
queryParams: Map<String, Any?> = emptyMap(),
queryParams: Map<String, Any?> = emptyMap()
): T? {
Log.info("GET request to $baseURL$path: $queryParams")
Log.info("GET request to [$baseURL$path] with request parameters: $queryParams")
val urlBuilder = "$baseURL$path".toHttpUrlOrNull()?.newBuilder() ?: return null
queryParams.forEach { (k, v) -> urlBuilder.addQueryParameter(k, v.toString()) }
val url = urlBuilder.build()
val request = Request.Builder().url(url).get().build()
return try {
client.newCall(request).execute().use {response ->
if (!response.isSuccessful) throw IOException(
"Unsuccessful response: ${response.code} ${response.message}"
)
response.body?.string()?.let {
json.decodeFromString<T>(it)
} ?: throw IOException("Empty response body")
var retries = MAX_RETRIES
while (retries > 0) {
try {
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException(
"Unsuccessful response: ${response.code} ${response.message}"
)
val result = response.body?.string()?.let {
json.decodeFromString<T>(it)
}
return result
}
} catch (e: IOException) {
Log.warn("$e, retrying...")
retries--
Thread.sleep(RETRY_INTERVAL)
} catch (e: Exception) {
Log.warn(e.toString())
return null
}
} catch (e: Exception) {
Log.warn(e.toString())
null
}
return null
}

private inline fun <reified T, reified R> post(path: String, body: T? = null): R? {
Log.debug("POST request to $baseURL$path: $body")
Log.info("POST request to [$baseURL$path] with request body: $body")
val url = "$baseURL$path".toHttpUrlOrNull() ?: return null
val requestBody = json.encodeToString(serializer(), body).toRequestBody("application/json".toMediaType())
val request = Request.Builder().url(url).post(requestBody).build()
return try {
val response: Response = client.newCall(request).execute()
if (response.isSuccessful) {
response.body?.let {json.decodeFromString<R>(it.string())}
} else null
} catch (e: Exception) {
Log.warn(e.toString())
null
var retries = MAX_RETRIES
while (retries > 0) {
try {
val response: Response = client.newCall(request).execute()
if (!response.isSuccessful) throw IOException(
"Unsuccessful response: ${response.code} ${response.message}"
)
val result = response.body?.let {
json.decodeFromString<R>(it.string())
}
return result
} catch (e: IOException) {
Log.warn("$e, retrying...")
retries--
Thread.sleep(RETRY_INTERVAL)
} catch (e: Exception) {
Log.warn(e.toString())
return null
}
}
return null
}

private inline fun <reified T, reified R> streamPost(path: String, body: T? = null): Flow<R> = callbackFlow<R> {
Log.debug("POST request to $baseURL$path: $body")
Log.info("POST request to [$baseURL$path] with request body: $body")
val url = "$baseURL$path".toHttpUrlOrNull() ?: return@callbackFlow
val requestJson = json.encodeToString(serializer(), body)
val requestBody = requestJson.toRequestBody("application/json".toMediaType())
Expand Down Expand Up @@ -311,8 +341,8 @@ class DevChatClient() {
return get("/workflows/config")
}
fun updateWorkflows() {
val response: Map<String, Any>? = post<Any, _>("/workflows/update")
Log.debug("Update workflows response: $response")
val response: UpdateWorkflowResponse? = post<String?, _>("/workflows/update")
Log.info("Update workflows response: $response")
}

fun insertLog(logEntry: LogEntry): LogInsertRes? {
Expand Down Expand Up @@ -356,11 +386,11 @@ class DevChatClient() {
}

fun deleteTopic(topicRootHash: String) {
val response: Map<String, Any>? = post("/topics/delete", mapOf(
val response: Map<String, String>? = post("/topics/delete", mapOf(
"topic_hash" to topicRootHash,
"workspace" to PathUtils.workspace,
))
Log.debug("deleteTopic response data: $response")
Log.info("deleteTopic response data: $response")
}

private fun cancelMessage() {
Expand Down
25 changes: 14 additions & 11 deletions src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ai.devchat.common.Notifier
import ai.devchat.common.OSInfo
import ai.devchat.common.PathUtils
import ai.devchat.core.DC_CLIENT
import ai.devchat.plugin.DevChatToolWindow
import ai.devchat.plugin.browser
import ai.devchat.storage.CONFIG
import ai.devchat.storage.DevChatState
Expand Down Expand Up @@ -39,17 +40,6 @@ class DevChatSetupThread : Thread() {
private fun setup(envManager: PythonEnvManager) {
val overwrite = devChatVersion != DevChatState.instance.lastVersion
PathUtils.copyResourceDirToPath("/tools/site-packages", PathUtils.sitePackagePath, overwrite)
PathUtils.copyResourceDirToPath(
"/tools/code-editor/${PathUtils.codeEditorBinary}",
Paths.get(PathUtils.toolsPath, PathUtils.codeEditorBinary).toString(),
overwrite
)
PathUtils.copyResourceDirToPath(
"/tools/sonar-rspec",
Paths.get(PathUtils.toolsPath, "sonar-rspec").toString(),
overwrite
)
PathUtils.copyResourceDirToPath("/workflows", PathUtils.workflowPath)
"python_for_chat".let{k ->
if (OSInfo.isWindows) {
val installDir = Paths.get(PathUtils.workPath, "python-win").toString()
Expand All @@ -67,7 +57,20 @@ class DevChatSetupThread : Thread() {
).pythonCommand
}
}
DevChatToolWindow.pythonReady = true
PathUtils.copyResourceDirToPath(
"/tools/code-editor/${PathUtils.codeEditorBinary}",
Paths.get(PathUtils.toolsPath, PathUtils.codeEditorBinary).toString(),
overwrite
)
PathUtils.copyResourceDirToPath(
"/tools/sonar-rspec",
Paths.get(PathUtils.toolsPath, "sonar-rspec").toString(),
overwrite
)
PathUtils.copyResourceDirToPath("/workflows", PathUtils.workflowPath)

DevChatToolWindow.pythonReady = true
try {
DC_CLIENT.updateWorkflows()
} catch (e: Exception) {
Expand Down
10 changes: 9 additions & 1 deletion src/main/kotlin/ai/devchat/plugin/DevChatToolWindow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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
Expand All @@ -20,6 +21,8 @@ import javax.swing.SwingConstants
class DevChatToolWindow : ToolWindowFactory, DumbAware, Disposable {
private var ideService: IDEServer? = null
private var localService: LocalService? = null
private val coroutineScope = CoroutineScope(Dispatchers.Default)


override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
currentProject = project
Expand All @@ -37,17 +40,22 @@ class DevChatToolWindow : ToolWindowFactory, DumbAware, Disposable {
toolWindow.contentManager.addContent(content)
DevChatSetupThread().start()
ideService = IDEServer(project).start()
localService = LocalService().start()
coroutineScope.launch {
while (!pythonReady) { delay(100) }
localService = LocalService().start()
}
}

override fun dispose() {
DevChatWrapper.activeChannel?.close()
coroutineScope.cancel()
ideService?.stop()
localService?.stop()
}

companion object {
var loaded: Boolean = false
var pythonReady: Boolean = false
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/ai/devchat/plugin/IDEServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ class IDEServer(private var project: Project) {
}

get("/current_file_info") {
val file: VirtualFile = project.getCurrentFile()
val file: VirtualFile? = project.getCurrentFile()
call.respond(Result(mapOf(
"path" to file.path,
"extension" to file.extension,
"path" to file?.path,
"extension" to file?.extension,
)))
}

Expand Down Expand Up @@ -392,7 +392,7 @@ fun Project.getDocument(filePath: String): Document = runInEdtAndGet {
}
}

fun Project.getCurrentFile(): VirtualFile = runInEdtAndGet {
fun Project.getCurrentFile(): VirtualFile? = runInEdtAndGet {
ReadAction.compute<VirtualFile, Throwable> {
val editor: Editor? = FileEditorManager.getInstance(this).selectedTextEditor
editor?.document?.let { document ->
Expand Down
Loading