Skip to content

Commit 2db9859

Browse files
authored
code clean up (#19)
refactor: improve exception handling and code readability across multiple files refactor: clean up code formatting and improve readability across multiple files refactor: update import statements for improved clarity and consistency in test files refactor: improve code readability by formatting log messages and restructuring conditional statements * test: add regression tests for Credits endpoint and modal dialog behavior * refactor: update Detekt plugin version and improve code readability in multiple files
1 parent 357bd29 commit 2db9859

File tree

94 files changed

+5216
-3432
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+5216
-3432
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5959

6060
#### IDE Support Extension
6161
- **JetBrains IDE 2025.3.X Support** - Extended compatibility for newest IDE versions including Rider
62-
- **Deprecated API Updates** - Replaced deprecated PluginDescriptor.isEnabled() with PluginManagerCore.isPluginEnabled()
62+
- **Deprecated API Updates** - Replaced deprecated PluginDescriptor.isEnabled() with PluginManagerCore.isDisabled() (negated)
6363
- **Backward Compatibility** - Seamless upgrade path for existing users from 2023.2+
6464

6565
#### Build System Updates

build.gradle.kts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import org.jetbrains.intellij.tasks.PublishPluginTask
2+
13
plugins {
24
id("java")
3-
id("org.jetbrains.kotlin.jvm") version "1.9.25"
5+
id("org.jetbrains.kotlin.jvm") version "2.1.10"
46
id("org.jetbrains.intellij") version "1.17.4"
5-
id("io.gitlab.arturbosch.detekt") version "1.23.4"
7+
id("io.gitlab.arturbosch.detekt") version "1.23.7"
68
}
79

810
group = project.findProperty("pluginGroup") ?: "org.zhavoronkov"
@@ -23,6 +25,9 @@ dependencies {
2325
implementation("com.squareup.okhttp3:okhttp:4.11.0")
2426
implementation("com.google.code.gson:gson:2.10.1")
2527

28+
// Use IntelliJ's bundled coroutines library to avoid classloader conflicts
29+
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
30+
2631
// Embedded HTTP server for AI Assistant integration
2732
implementation("org.eclipse.jetty:jetty-server:11.0.18")
2833
implementation("org.eclipse.jetty:jetty-servlet:11.0.18")
@@ -34,12 +39,13 @@ dependencies {
3439
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
3540
testImplementation("org.mockito:mockito-core:5.7.0")
3641
testImplementation("org.mockito:mockito-junit-jupiter:5.7.0")
42+
testImplementation("org.mockito.kotlin:mockito-kotlin:5.1.0")
3743
testImplementation("com.squareup.okhttp3:mockwebserver:4.11.0")
3844
testImplementation("org.assertj:assertj-core:3.24.2")
3945

4046

4147
// Detekt plugins
42-
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4")
48+
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7")
4349
}
4450

4551
// Configure IntelliJ Plugin
@@ -58,6 +64,13 @@ detekt {
5864
baseline = file("$projectDir/config/detekt/baseline.xml")
5965
}
6066

67+
// Configure publishing token for publishPlugin task
68+
val publishPluginTask = tasks.named<PublishPluginTask>("publishPlugin")
69+
publishPluginTask.configure {
70+
val publishToken = System.getenv("PUBLISH_TOKEN") ?: project.findProperty("publishToken") as String?
71+
publishToken?.let { token.set(it) }
72+
}
73+
6174
// Configure Detekt SARIF reporting task
6275
tasks.register<io.gitlab.arturbosch.detekt.Detekt>("detektSarif") {
6376
description = "Runs detekt and generates SARIF report for GitHub Code Scanning"

config/detekt/detekt.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ formatting:
947947
autoCorrect: true
948948
NoWildcardImports:
949949
active: true
950-
packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**'
950+
packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**,kotlinx.coroutines.**,com.intellij.ui.dsl.builder.**,com.intellij.openapi.ui.popup.**'
951951
NullableTypeSpacing:
952952
active: true
953953
autoCorrect: true

docs/AI_ASSISTANT_TECHNICAL_GUIDE.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,11 @@ PluginLogger.Service.debug("[AI Assistant] Integration status: ...")
218218
```kotlin
219219
private fun checkAIAssistantInstalled(): Boolean {
220220
return try {
221-
val pluginManager = com.intellij.ide.plugins.PluginManagerCore.getPlugins()
222-
val aiPlugin = pluginManager.find {
223-
it.pluginId.idString == "com.intellij.ml.llm"
224-
}
225-
PluginLogger.Settings.debug("AI Assistant plugin: $aiPlugin, enabled: ${aiPlugin?.isEnabled}")
226-
aiPlugin != null && aiPlugin.isEnabled
221+
val pluginId = com.intellij.openapi.extensions.PluginId.getId("com.intellij.ml.llm")
222+
val aiPlugin = com.intellij.ide.plugins.PluginManagerCore.getPlugin(pluginId)
223+
val isEnabled = aiPlugin != null && !com.intellij.ide.plugins.PluginManagerCore.isDisabled(pluginId)
224+
PluginLogger.Settings.debug("AI Assistant plugin: $aiPlugin, enabled: $isEnabled")
225+
isEnabled
227226
} catch (e: Exception) {
228227
PluginLogger.Settings.debug("Could not check AI Assistant plugin status: ${e.message}")
229228
false

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ platformVersion = 2024.1.6
2626
platformPlugins =
2727

2828
# Gradle Releases -> https://github.com/gradle/gradle/releases
29-
gradleVersion = 8.14
29+
gradleVersion = 8.9
3030

3131
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
3232
kotlin.stdlib.default.dependency = false
3333

3434
# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
35-
org.gradle.configuration-cache = true
35+
org.gradle.configuration-cache = false
3636

3737
# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
3838
org.gradle.caching = true

src/main/kotlin/org/zhavoronkov/openrouter/aiassistant/OpenRouterChatContextProvider.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class OpenRouterChatContextProvider {
1515
companion object {
1616
private const val PROVIDER_ID = "openrouter"
1717
private const val PROVIDER_NAME = "OpenRouter"
18+
private const val APPROXIMATE_MODEL_COUNT = 400
1819
}
1920

2021
/**
@@ -29,15 +30,19 @@ class OpenRouterChatContextProvider {
2930

3031
/**
3132
* Checks if the provider is available and configured
33+
* @param project The project context (currently unused, reserved for future use)
3234
*/
3335
fun isAvailable(project: Project?): Boolean {
36+
// Project parameter reserved for future project-specific configuration
3437
return settingsService.isConfigured()
3538
}
3639

3740
/**
3841
* Provides context information for chat conversations
42+
* @param project The project context (currently unused, reserved for future use)
3943
*/
4044
fun getContextInfo(project: Project?): Map<String, Any> {
45+
// Project parameter reserved for future project-specific context
4146
PluginLogger.Settings.debug("Providing chat context for OpenRouter")
4247

4348
return mapOf(
@@ -52,7 +57,7 @@ class OpenRouterChatContextProvider {
5257
"streaming",
5358
"multiple_models"
5459
),
55-
"modelCount" to 400 // OpenRouter supports 400+ models
60+
"modelCount" to APPROXIMATE_MODEL_COUNT
5661
)
5762
}
5863

src/main/kotlin/org/zhavoronkov/openrouter/aiassistant/OpenRouterChatModelProvider.kt

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ class OpenRouterChatModelProvider {
1919
private const val MAX_TOKENS_DEFAULT = 2000
2020
private const val TEMPERATURE_DEFAULT = 0.7
2121
private const val SUPPORTS_STREAMING = true
22+
23+
// Token estimation: ~4 characters per token for most models
24+
private const val CHARS_PER_TOKEN = 4
25+
26+
// HTTP client timeouts (in seconds)
27+
private const val CONNECT_TIMEOUT_SECONDS = 30L
28+
private const val READ_TIMEOUT_SECONDS = 60L
2229
}
2330

2431
/**
@@ -83,16 +90,19 @@ class OpenRouterChatModelProvider {
8390

8491
/**
8592
* Check if streaming is supported for the given model
93+
* @param modelId The model ID (currently unused, all models support streaming)
8694
*/
87-
fun supportsStreaming(_modelId: String): Boolean = SUPPORTS_STREAMING
95+
fun supportsStreaming(modelId: String): Boolean {
96+
// All OpenRouter models support streaming, parameter reserved for future model-specific logic
97+
return SUPPORTS_STREAMING
98+
}
8899

89100
/**
90101
* Get the estimated token count for a message
91102
* This is a rough approximation since we don't have access to the exact tokenizer
92103
*/
93104
fun estimateTokenCount(text: String): Int {
94-
// Rough estimation: ~4 characters per token for most models
95-
return (text.length / 4).coerceAtLeast(1)
105+
return (text.length / CHARS_PER_TOKEN).coerceAtLeast(1)
96106
}
97107

98108
private fun createChatRequestBody(
@@ -122,8 +132,8 @@ class OpenRouterChatModelProvider {
122132
)
123133

124134
val client = okhttp3.OkHttpClient.Builder()
125-
.connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
126-
.readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
135+
.connectTimeout(CONNECT_TIMEOUT_SECONDS, java.util.concurrent.TimeUnit.SECONDS)
136+
.readTimeout(READ_TIMEOUT_SECONDS, java.util.concurrent.TimeUnit.SECONDS)
127137
.build()
128138

129139
client.newCall(request).execute().use { response ->
@@ -142,38 +152,43 @@ class OpenRouterChatModelProvider {
142152

143153
private fun parseOpenRouterResponse(responseBody: String?): ChatResponse {
144154
return try {
145-
if (responseBody.isNullOrBlank()) {
146-
return ChatResponse.error("Empty response from OpenRouter")
155+
when {
156+
responseBody.isNullOrBlank() -> ChatResponse.error("Empty response from OpenRouter")
157+
else -> parseValidResponse(responseBody)
147158
}
159+
} catch (e: Exception) {
160+
PluginLogger.Service.error("Error parsing OpenRouter response", e)
161+
ChatResponse.error("Response parsing failed: ${e.message}")
162+
}
163+
}
148164

149-
val responseMap = gson.fromJson(responseBody, Map::class.java) as? Map<String, Any>
150-
?: return ChatResponse.error("Invalid response format")
151-
152-
// Check for error in response
153-
val error = responseMap["error"] as? Map<String, Any>
154-
if (error != null) {
155-
val errorMessage = error["message"] as? String ?: "Unknown error"
156-
return ChatResponse.error(errorMessage)
157-
}
165+
private fun parseValidResponse(responseBody: String): ChatResponse {
166+
val responseMap = gson.fromJson(responseBody, Map::class.java) as? Map<String, Any>
167+
?: return ChatResponse.error("Invalid response format")
168+
return parseResponseMap(responseMap)
169+
}
158170

159-
// Parse successful response
160-
val choices = responseMap["choices"] as? List<Map<String, Any>>
161-
if (choices.isNullOrEmpty()) {
162-
return ChatResponse.error("No choices in response")
163-
}
171+
private fun parseResponseMap(responseMap: Map<String, Any>): ChatResponse {
172+
// Check for error in response
173+
val error = responseMap["error"] as? Map<String, Any>
174+
if (error != null) {
175+
val errorMessage = error["message"] as? String ?: "Unknown error"
176+
return ChatResponse.error(errorMessage)
177+
}
164178

179+
// Parse successful response
180+
val choices = responseMap["choices"] as? List<Map<String, Any>>
181+
val content = if (choices != null && choices.isNotEmpty()) {
165182
val firstChoice = choices[0]
166183
val message = firstChoice["message"] as? Map<String, Any>
167-
val content = message?.get("content") as? String
168-
169-
if (content != null) {
170-
ChatResponse.success(content)
171-
} else {
172-
ChatResponse.error("No content in response")
173-
}
174-
} catch (e: Exception) {
175-
PluginLogger.Service.error("Error parsing OpenRouter response", e)
176-
ChatResponse.error("Response parsing failed: ${e.message}")
184+
message?.get("content") as? String
185+
} else {
186+
null
187+
}
188+
return if (content != null) {
189+
ChatResponse.success(content)
190+
} else {
191+
ChatResponse.error("No choices or content in response")
177192
}
178193
}
179194
}

0 commit comments

Comments
 (0)