make models first class citizens

master
io42630 1 week ago
parent 656001f2b2
commit 576eb12098

@ -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.

@ -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"))
}

@ -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))

@ -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
}
}

@ -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<L3PersistentState> {
@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 {

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.plexworlds.l3.config.L3SettingsPanel">
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="8" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
<xy x="20" y="20" width="741" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1f787" class="javax.swing.JTextField" binding="modelField">
<component id="1f787" class="javax.swing.JTextField" binding="modelOverrideField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
@ -18,47 +18,85 @@
</component>
<vspacer id="e63c3">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="7" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="c9628" class="javax.swing.JLabel">
<component id="766bf" class="javax.swing.JComboBox" binding="providerComboBox">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Model"/>
<model/>
</properties>
</component>
<component id="5e8ed" class="javax.swing.JTextField" binding="urlField">
<component id="76509" class="javax.swing.JComboBox" binding="modelComboBox">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="69f35" class="javax.swing.JLabel">
<component id="a61d1" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="6" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="URL"/>
<text value="Override if you would like to try another model."/>
</properties>
</component>
<component id="766bf" class="javax.swing.JComboBox" binding="providerComboBox">
<component id="4b8a2" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<model/>
<text value="Provider"/>
</properties>
</component>
<component id="3e38d" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Provider"/>
<text value="What type of server is it?"/>
</properties>
</component>
<component id="fb5e2" class="javax.swing.JLabel">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Model"/>
</properties>
</component>
<component id="d05ec" class="javax.swing.JLabel">
<constraints>
<grid row="5" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Supported models."/>
</properties>
</component>
<component id="69f35" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="URL"/>
</properties>
</component>
<component id="5e8ed" class="javax.swing.JTextField" binding="urlField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="c9628" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="URL of the server hosting your LLMs."/>
</properties>
</component>
</children>

@ -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<String>
lateinit var modelField: JTextField
lateinit var modelComboBox: JComboBox<String>
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)
}
}

@ -0,0 +1,17 @@
package com.plexworlds.l3.llm.model
abstract class Codellama: Model {
override fun prefix(): String {
return "<PRE>"
}
override fun suffix(): String {
return "<SUF>"
}
override fun middle(): String {
return "<MID>"
}
}

@ -0,0 +1,7 @@
package com.plexworlds.l3.llm.model
class Codellama7b: Codellama() {
override fun name(): String {
return "codellama:7b"
}
}

@ -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
}
}

@ -0,0 +1,13 @@
package com.plexworlds.l3.llm.model
interface Model {
fun prefix(): String
fun suffix(): String
fun middle(): String
fun name(): String
}

@ -0,0 +1,7 @@
package com.plexworlds.l3.llm.model
enum class Models(val model: Model) {
QWEN25CODER_3B(Qwen25Coder3b()),
CODELLAMA_7B(Codellama7b())
}

@ -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|>"
}
}

@ -0,0 +1,10 @@
package com.plexworlds.l3.llm.model
class Qwen25Coder1_5b : Qwen25Coder() {
override fun name(): String {
return "qwen2.5-coder:1.5b"
}
}

@ -0,0 +1,10 @@
package com.plexworlds.l3.llm.model
class Qwen25Coder3b : Qwen25Coder() {
override fun name(): String {
return "qwen2.5-coder:3b"
}
}

@ -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
}
}

@ -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 ""
}
}

@ -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 "<No connection to Ollama API>"
}
for (i in 0..<RETRY_COUNT) {
val suggestion = try {
val lama = OllamaAPI(HOST)
OllamaAPI(HOST).apply {
OllamaAPI(host).apply {
setRequestTimeoutSeconds(4)
}.generate(model, "<PRE> $prefix <SUF>$suffix <MID>", 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 = "<EOT>"

@ -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
}

@ -3,5 +3,5 @@ package com.plexworlds.l3.llm.provider
enum class Providers(val provider: Provider) {
OLLAMA(Ollama),
DUMMY(Dummy)
DUMMY(DummyProvider)
}

Loading…
Cancel
Save