From 576eb12098a2abff7c887df2419ddd8c1d07aba9 Mon Sep 17 00:00:00 2001 From: io42630 Date: Fri, 16 May 2025 16:57:59 +0200 Subject: [PATCH] make models first class citizens --- README.md | 4 + build.gradle.kts | 1 + .../l3/L3InlineCompletionProvider.kt | 11 ++- .../com/plexworlds/l3/config/L3Config.kt | 44 ++++++----- .../plexworlds/l3/config/L3PersistentState.kt | 17 ++-- .../plexworlds/l3/config/L3SettingsPanel.form | 78 ++++++++++++++----- .../plexworlds/l3/config/L3SettingsPanel.kt | 6 +- .../com/plexworlds/l3/llm/model/Codellama.kt | 17 ++++ .../plexworlds/l3/llm/model/Codellama7b.kt | 7 ++ .../com/plexworlds/l3/llm/model/DummyModel.kt | 21 +++++ .../com/plexworlds/l3/llm/model/Model.kt | 13 ++++ .../com/plexworlds/l3/llm/model/Models.kt | 7 ++ .../plexworlds/l3/llm/model/Qwen25Coder.kt | 17 ++++ .../l3/llm/model/Qwen25Coder1_5b.kt | 10 +++ .../plexworlds/l3/llm/model/Qwen25Coder3b.kt | 10 +++ .../com/plexworlds/l3/llm/provider/Dummy.kt | 18 ----- .../l3/llm/provider/DummyProvider.kt | 23 ++++++ .../com/plexworlds/l3/llm/provider/Ollama.kt | 33 ++++---- .../plexworlds/l3/llm/provider/Provider.kt | 23 ++---- .../plexworlds/l3/llm/provider/Providers.kt | 2 +- 20 files changed, 262 insertions(+), 100 deletions(-) create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Codellama.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Codellama7b.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/DummyModel.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Model.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Models.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder1_5b.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder3b.kt delete mode 100644 src/main/kotlin/com/plexworlds/l3/llm/provider/Dummy.kt create mode 100644 src/main/kotlin/com/plexworlds/l3/llm/provider/DummyProvider.kt diff --git a/README.md b/README.md index e977e28..f48de98 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ # Local Llama Link + +* The goal of this project is to _link_ a local ollama instance to your IDE. + +* Nothing more, nothing less. diff --git a/build.gradle.kts b/build.gradle.kts index e4c754f..d61b35d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation("org.slf4j:slf4j-jdk14:2.1.0-alpha1") implementation("com.google.guava:guava:33.4.8-jre") implementation("dev.langchain4j:langchain4j:0.36.2") + implementation("org.apache.commons:commons-lang3:3.17.0") testImplementation(kotlin("test")) testImplementation(kotlin("test-junit")) } diff --git a/src/main/kotlin/com/plexworlds/l3/L3InlineCompletionProvider.kt b/src/main/kotlin/com/plexworlds/l3/L3InlineCompletionProvider.kt index a30023c..43a72f8 100644 --- a/src/main/kotlin/com/plexworlds/l3/L3InlineCompletionProvider.kt +++ b/src/main/kotlin/com/plexworlds/l3/L3InlineCompletionProvider.kt @@ -27,8 +27,15 @@ class L3InlineCompletionProvider : InlineCompletionProvider { val (prefix, suffix) = request.document.text.splitUsingOffset(request.startOffset) val lastPrefixLine = prefix.lines().last() - val llm = L3PersistentState.getInstance().provider - val suggestion = llm.call(prefix, suffix) + val pState = L3PersistentState.getInstance() + + val suggestion = pState.provider.call( + pState.url, + pState.model, + prefix, + suffix + ) + launch { try { trySend(InlineCompletionGrayTextElement(suggestion)) diff --git a/src/main/kotlin/com/plexworlds/l3/config/L3Config.kt b/src/main/kotlin/com/plexworlds/l3/config/L3Config.kt index 62f2ce5..16f0fa8 100644 --- a/src/main/kotlin/com/plexworlds/l3/config/L3Config.kt +++ b/src/main/kotlin/com/plexworlds/l3/config/L3Config.kt @@ -1,7 +1,7 @@ package com.plexworlds.l3.config -import com.plexworlds.l3.llm.provider.Ollama import com.intellij.openapi.options.SearchableConfigurable +import com.plexworlds.l3.llm.model.Models import com.plexworlds.l3.llm.provider.Providers import javax.swing.JComponent import org.slf4j.Logger @@ -20,38 +20,42 @@ class L3Config : SearchableConfigurable { } override fun isModified(): Boolean { - if (panel == null) { - logger.error("L3SettingsPanel is null") - return false - } + val panel = this.panel ?: return false val l3PersistentState = L3PersistentState.getInstance() - val isProviderModified = panel?.providerComboBox?.selectedItem != l3PersistentState.provider - val isModelModified = panel?.modelField?.text != l3PersistentState.model; - val isUrlModified = panel?.urlField?.text != l3PersistentState.url - val isModified = isProviderModified || isModelModified || isUrlModified; - logger.debug("isModified: $isModified") - return isModified + val isProviderModified = panel.providerComboBox.selectedItem != l3PersistentState.provider + val isModelModified = panel.modelComboBox.selectedItem != l3PersistentState.model; + val isModelOverrideModified = panel.modelOverrideField.text != l3PersistentState.modelOverride; + val isUrlModified = panel.urlField.text != l3PersistentState.url + return isProviderModified || isModelModified || isUrlModified || isModelOverrideModified } override fun reset() { val panel = this.panel ?: return val l3PersistentState = L3PersistentState.getInstance() - panel.modelField.text = l3PersistentState.model - Ollama.changeModel(l3PersistentState.model) + panel.providerComboBox.selectedItem = l3PersistentState.provider + panel.modelComboBox.selectedItem = l3PersistentState.model + panel.modelOverrideField.text = l3PersistentState.modelOverride + panel.urlField.text = l3PersistentState.url } override fun apply() { val panel = this.panel ?: return - val state = L3PersistentState.getInstance() + val l3PersistentState = L3PersistentState.getInstance() - val llm = Providers.valueOf(panel.providerComboBox.selectedItem as String).provider - val model = panel.modelField.text + l3PersistentState.url = panel.urlField.text - llm.changeModel(model) + l3PersistentState.provider = Providers + .valueOf(panel.providerComboBox.selectedItem as String).provider - state.provider = llm - state.model = model - state.url = panel.urlField.text + + if (panel.modelOverrideField.text.isNotEmpty()) { + l3PersistentState.modelOverride = panel.modelOverrideField.text + l3PersistentState.model = Models + .valueOf(panel.modelOverrideField.text).model + } else { + l3PersistentState.model = Models + .valueOf(panel.modelComboBox.selectedItem as String).model + } } diff --git a/src/main/kotlin/com/plexworlds/l3/config/L3PersistentState.kt b/src/main/kotlin/com/plexworlds/l3/config/L3PersistentState.kt index 1b6f137..1f931ca 100644 --- a/src/main/kotlin/com/plexworlds/l3/config/L3PersistentState.kt +++ b/src/main/kotlin/com/plexworlds/l3/config/L3PersistentState.kt @@ -6,7 +6,9 @@ import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.Storage import com.intellij.openapi.components.State import com.intellij.util.xmlb.XmlSerializerUtil -import com.plexworlds.l3.llm.provider.Dummy +import com.plexworlds.l3.llm.model.DummyModel +import com.plexworlds.l3.llm.model.Model +import com.plexworlds.l3.llm.provider.DummyProvider import com.plexworlds.l3.llm.provider.Provider /** @@ -19,19 +21,24 @@ import com.plexworlds.l3.llm.provider.Provider class L3PersistentState : PersistentStateComponent { @JvmField - var model: String = "codellama:7b-code" + var model: Model = DummyModel @JvmField - var url: String = "http://localhost:11434" + var modelOverride: String = "" @JvmField - var provider: Provider = Dummy + var url: String = "" + + @JvmField + var provider: Provider = DummyProvider override fun getState(): L3PersistentState = this override fun loadState(l3PersistentState: L3PersistentState) { XmlSerializerUtil.copyBean(l3PersistentState, this) - Ollama.changeModel(l3PersistentState.model) + + + } companion object { diff --git a/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.form b/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.form index 6d603ba..a07da0c 100644 --- a/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.form +++ b/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.form @@ -1,16 +1,16 @@
- + - + - + - + @@ -18,47 +18,85 @@ - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.kt b/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.kt index 445f667..2a9dfbc 100644 --- a/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.kt +++ b/src/main/kotlin/com/plexworlds/l3/config/L3SettingsPanel.kt @@ -1,6 +1,7 @@ package com.plexworlds.l3.config; import com.intellij.ui.IdeBorderFactory +import com.plexworlds.l3.llm.model.Models import com.plexworlds.l3.llm.provider.Providers import javax.swing.JComboBox import javax.swing.JPanel @@ -10,12 +11,15 @@ class L3SettingsPanel { lateinit var mainPanel: JPanel lateinit var providerComboBox: JComboBox - lateinit var modelField: JTextField + lateinit var modelComboBox: JComboBox + lateinit var modelOverrideField: JTextField lateinit var urlField: JTextField init { mainPanel.border = IdeBorderFactory.createTitledBorder("Plugin Settings") providerComboBox.addItem(Providers.OLLAMA.name) providerComboBox.addItem(Providers.DUMMY.name) + modelComboBox.addItem(Models.QWEN25CODER_3B.name) + modelComboBox.addItem(Models.CODELLAMA_7B.name) } } diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama.kt new file mode 100644 index 0000000..41c6ea7 --- /dev/null +++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama.kt @@ -0,0 +1,17 @@ +package com.plexworlds.l3.llm.model + +abstract class Codellama: Model { + override fun prefix(): String { + return "
"
+    }
+
+    override fun suffix(): String {
+        return ""
+    }
+
+    override fun middle(): String {
+        return ""
+    }
+
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama7b.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama7b.kt
new file mode 100644
index 0000000..ecc68a4
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Codellama7b.kt
@@ -0,0 +1,7 @@
+package com.plexworlds.l3.llm.model
+
+class Codellama7b: Codellama()   {
+    override fun name(): String {
+        return "codellama:7b"
+    }
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/DummyModel.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/DummyModel.kt
new file mode 100644
index 0000000..98a4434
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/DummyModel.kt
@@ -0,0 +1,21 @@
+package com.plexworlds.l3.llm.model
+
+import org.apache.commons.lang3.StringUtils.EMPTY
+
+object DummyModel : Model {
+    override fun prefix(): String {
+        return EMPTY
+    }
+
+    override fun suffix(): String {
+        return EMPTY
+    }
+
+    override fun middle(): String {
+        return EMPTY
+    }
+
+    override fun name(): String {
+        return EMPTY
+    }
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Model.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Model.kt
new file mode 100644
index 0000000..9905b31
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Model.kt
@@ -0,0 +1,13 @@
+package com.plexworlds.l3.llm.model
+
+interface Model {
+
+    fun prefix(): String
+
+    fun suffix(): String
+
+    fun middle(): String
+
+    fun name(): String
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Models.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Models.kt
new file mode 100644
index 0000000..a63ca94
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Models.kt
@@ -0,0 +1,7 @@
+package com.plexworlds.l3.llm.model
+
+enum class Models(val model: Model) {
+
+    QWEN25CODER_3B(Qwen25Coder3b()),
+    CODELLAMA_7B(Codellama7b())
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder.kt
new file mode 100644
index 0000000..30eab6b
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder.kt
@@ -0,0 +1,17 @@
+package com.plexworlds.l3.llm.model
+
+abstract class Qwen25Coder : Model {
+    override fun prefix(): String {
+        return "<|fim_prefix|>"
+    }
+
+    override fun suffix(): String {
+        return "<|fim_suffix|>"
+    }
+
+    override fun middle(): String {
+        return "<|fim_middle|>"
+    }
+
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder1_5b.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder1_5b.kt
new file mode 100644
index 0000000..2f3a6d4
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder1_5b.kt
@@ -0,0 +1,10 @@
+package com.plexworlds.l3.llm.model
+
+
+class Qwen25Coder1_5b : Qwen25Coder() {
+    override fun name(): String {
+        return "qwen2.5-coder:1.5b"
+    }
+
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder3b.kt b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder3b.kt
new file mode 100644
index 0000000..8b329f1
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/model/Qwen25Coder3b.kt
@@ -0,0 +1,10 @@
+package com.plexworlds.l3.llm.model
+
+
+class Qwen25Coder3b : Qwen25Coder() {
+    override fun name(): String {
+        return "qwen2.5-coder:3b"
+    }
+
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/provider/Dummy.kt b/src/main/kotlin/com/plexworlds/l3/llm/provider/Dummy.kt
deleted file mode 100644
index 138780b..0000000
--- a/src/main/kotlin/com/plexworlds/l3/llm/provider/Dummy.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.plexworlds.l3.llm.provider
-
-
-object Dummy : Provider {
-
-    private var model = ""
-
-
-    override fun call(prefix: String, suffix: String): String {
-        return "Hello!"
-    }
-
-
-    override fun changeModel(model: String) {
-        Dummy.model = model
-    }
-
-}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/provider/DummyProvider.kt b/src/main/kotlin/com/plexworlds/l3/llm/provider/DummyProvider.kt
new file mode 100644
index 0000000..1d189fd
--- /dev/null
+++ b/src/main/kotlin/com/plexworlds/l3/llm/provider/DummyProvider.kt
@@ -0,0 +1,23 @@
+package com.plexworlds.l3.llm.provider
+
+import com.plexworlds.l3.llm.model.Model
+
+
+object DummyProvider : Provider {
+
+
+
+
+    override fun call(
+        host: String,
+        model: Model,
+        prefix: String,
+        suffix: String
+    ): String {
+        return ""
+    }
+
+
+
+
+}
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/provider/Ollama.kt b/src/main/kotlin/com/plexworlds/l3/llm/provider/Ollama.kt
index 0c6d1ee..c31397e 100644
--- a/src/main/kotlin/com/plexworlds/l3/llm/provider/Ollama.kt
+++ b/src/main/kotlin/com/plexworlds/l3/llm/provider/Ollama.kt
@@ -1,5 +1,6 @@
 package com.plexworlds.l3.llm.provider
 
+import com.plexworlds.l3.llm.model.Model
 import io.github.ollama4j.OllamaAPI
 import io.github.ollama4j.utils.Options
 import io.github.ollama4j.utils.OptionsBuilder
@@ -7,20 +8,28 @@ import java.net.http.HttpTimeoutException
 
 object Ollama : Provider {
 
-    private var model = "codellama:7b-code"
 
 
-    override fun call(prefix: String, suffix: String): String {
-        if (!isPingOk) {
+    override fun call(
+        host: String,
+        model: Model,
+        prefix: String,
+        suffix: String
+    ): String {
+        if (!isPingOk(host)) {
             return ""
         }
 
         for (i in 0.. $prefix $suffix ", null).response.let {
+                }.generate(
+                    model.name(),
+                    "${model.prefix()} $prefix ${model.suffix()}$suffix ${model.middle()}",
+                    true,
+                    options
+                ).response.let {
                     if (it.endsWith(END)) it.substring(0, it.length - END.length).trim(' ', '\t', '\n') else it
                 }
             } catch (e: HttpTimeoutException) {
@@ -35,9 +44,6 @@ object Ollama : Provider {
     }
 
 
-    override fun changeModel(model: String) {
-        Ollama.model = model
-    }
 
 
     private val options: Options by lazy {
@@ -47,18 +53,15 @@ object Ollama : Provider {
     }
 
 
-    private val isPingOk: Boolean
-        get() {
-            return try {
-                OllamaAPI(HOST).ping()
+    private fun isPingOk(host: String): Boolean {
+        return try {
+                OllamaAPI(host).ping()
             } catch (e: Exception) {
                 false
             }
         }
 
 
-    private const val HOST = "http://localhost:44000/"
-
 
     private const val END = ""
 
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/provider/Provider.kt b/src/main/kotlin/com/plexworlds/l3/llm/provider/Provider.kt
index c26b07f..a581f7b 100644
--- a/src/main/kotlin/com/plexworlds/l3/llm/provider/Provider.kt
+++ b/src/main/kotlin/com/plexworlds/l3/llm/provider/Provider.kt
@@ -1,23 +1,10 @@
 package com.plexworlds.l3.llm.provider
 
-/**
- * This is an interface for a Large Language Model (Provider).
- * It provides a method to generate a completion suggestion based on a given prefix and suffix.
- */
+import com.plexworlds.l3.llm.model.Model
+
+
 interface Provider {
-    /**
-     * This method generates a completion suggestion based on a given prefix and suffix.
-     *
-     * @param prefix The part of the code before the cursor.
-     * @param suffix The part of the code after the cursor.
-     * @return The generated completion suggestion, or null if no suggestion could be generated.
-     */
-    fun call(prefix: String, suffix: String): String
 
-    /**
-     * This method changes the current model used by the Provider.
-     *
-     * @param model The name of the model to be used.
-     */
-    fun changeModel(model: String)
+    fun call(host: String, model: Model, prefix: String, suffix: String): String
+
 }
diff --git a/src/main/kotlin/com/plexworlds/l3/llm/provider/Providers.kt b/src/main/kotlin/com/plexworlds/l3/llm/provider/Providers.kt
index 65da985..f0ead29 100644
--- a/src/main/kotlin/com/plexworlds/l3/llm/provider/Providers.kt
+++ b/src/main/kotlin/com/plexworlds/l3/llm/provider/Providers.kt
@@ -3,5 +3,5 @@ package com.plexworlds.l3.llm.provider
 enum class Providers(val provider: Provider) {
 
     OLLAMA(Ollama),
-    DUMMY(Dummy)
+    DUMMY(DummyProvider)
 }