Skip to content

Commit

Permalink
Add conversation class and json parsing for llm responses
Browse files Browse the repository at this point in the history
  • Loading branch information
fmueller committed May 19, 2024
1 parent 2c4f3cd commit af7c9a7
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 18 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins {
alias(libs.plugins.changelog) // Gradle Changelog Plugin
alias(libs.plugins.qodana) // Gradle Qodana Plugin
alias(libs.plugins.kover) // Gradle Kover Plugin
kotlin("plugin.serialization") version "1.9.24"
}

group = properties("pluginGroup").get()
Expand All @@ -21,6 +22,7 @@ repositories {
}

dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation(libs.flexmark)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.fmueller.jarvis.conversation

import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport
import java.time.LocalDateTime

enum class Role {
ASSISTANT, USER
}

data class Message(val role: Role, val content: String, val createdAt: LocalDateTime = LocalDateTime.now())

class Conversation {

private val messages = mutableListOf<Message>()
private val propertyChangeSupport = PropertyChangeSupport(this)

fun addMessage(message: Message) {
val oldMessages = ArrayList(messages)
messages.add(message)
propertyChangeSupport.firePropertyChange("message", oldMessages, ArrayList(messages))
}

fun getMessages() = messages.toList()

fun addPropertyChangeListener(listener: PropertyChangeListener) {
propertyChangeSupport.addPropertyChangeListener(listener)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.thisLogger
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.util.*

@Serializable
private data class Response(val message: Message)

@Serializable
private data class Message(val role: String, val content: String)

@Service(Service.Level.PROJECT)
class OllamaService : Disposable {

Expand Down Expand Up @@ -66,10 +75,10 @@ class OllamaService : Disposable {
)
.build()

val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val body = response.body()
thisLogger().warn("Response: $body")
body
val httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString())
val json = Json { ignoreUnknownKeys = true }
val response = json.decodeFromString<Response>(httpResponse.body())
response.message.content
} catch (e: Exception) {
"Error: ${e.message}"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.github.fmueller.jarvis.toolWindow

import com.github.fmueller.jarvis.conversation.Conversation
import com.github.fmueller.jarvis.conversation.Message
import com.github.fmueller.jarvis.conversation.Role
import com.github.fmueller.jarvis.services.OllamaService
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
Expand All @@ -21,10 +24,7 @@ import java.awt.event.FocusEvent
import java.awt.event.FocusListener
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import javax.swing.BorderFactory
import javax.swing.JEditorPane
import javax.swing.JPanel
import javax.swing.UIManager
import javax.swing.*
import javax.swing.border.AbstractBorder

class ConversationWindowFactory : ToolWindowFactory {
Expand All @@ -41,21 +41,33 @@ class ConversationWindowFactory : ToolWindowFactory {

private val project = toolWindow.project
private val ollama = project.service<OllamaService>()
private val conversation = Conversation()

fun getContent() = BorderLayoutPanel().apply {

val conversationPanel = JEditorPane().apply {
editorKit = HTMLEditorKitBuilder.simple()
text = markdownToHtml(
"""
private val conversationPanel = JEditorPane().apply {
editorKit = HTMLEditorKitBuilder.simple()
text = markdownToHtml(
"""
## Hello!
I am Jarvis, your personal coding assistant. I try to be helpful, but I am not perfect.
""".trimIndent()
)
isEditable = false
)
isEditable = false
}

init {
conversation.addPropertyChangeListener {
if (it.propertyName == "message") {
val messages = conversation.getMessages()
SwingUtilities.invokeLater {
conversationPanel.text =
markdownToHtml(messages.joinToString("\n") { "**" + it.role.toString() + "**\n" + it.content })

Check notice on line 64 in src/main/kotlin/com/github/fmueller/jarvis/toolWindow/ConversationWindowFactory.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nested lambda has shadowed implicit parameter

Implicit parameter 'it' of enclosing lambda is shadowed

Check notice on line 64 in src/main/kotlin/com/github/fmueller/jarvis/toolWindow/ConversationWindowFactory.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nested lambda has shadowed implicit parameter

Implicit parameter 'it' of enclosing lambda is shadowed
}
}
}
}

fun getContent() = BorderLayoutPanel().apply {
var borderColor = JBColor.GRAY
val inputArea = JBTextArea().apply {
lineWrap = true
Expand Down Expand Up @@ -90,8 +102,12 @@ class ConversationWindowFactory : ToolWindowFactory {
override fun keyPressed(e: KeyEvent) {
if (e.keyCode == KeyEvent.VK_ENTER) {
val question = inputArea.text
conversation.addMessage(Message(Role.USER, question))

inputArea.text = ""
conversationPanel.text = markdownToHtml(ollama.ask(question))

val answer = ollama.ask(question)
conversation.addMessage(Message(Role.ASSISTANT, answer))
}
}
})
Expand All @@ -112,7 +128,7 @@ class ConversationWindowFactory : ToolWindowFactory {
Parser.EXTENSIONS,
listOf(TablesExtension.create(), StrikethroughExtension.create())
)
options.set(HtmlRenderer.SOFT_BREAK, "<br />\n")
options.set(HtmlRenderer.SOFT_BREAK, "<br />")
val parser: Parser = Parser.builder(options).build()
HtmlRenderer.builder(options).build().render(parser.parse(text))
}
Expand Down

0 comments on commit af7c9a7

Please sign in to comment.