Skip to content

Commit

Permalink
feat: Add LangChain4J and walking skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
fmueller committed Sep 6, 2024
1 parent 60783db commit bf333d3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 15 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ repositories {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.2")
implementation(libs.flexmark)
implementation("dev.langchain4j:langchain4j:0.34.0")
implementation("dev.langchain4j:langchain4j-ollama:0.34.0")
}

kotlin {
Expand Down
64 changes: 49 additions & 15 deletions src/main/kotlin/com/github/fmueller/jarvis/ai/OllamaService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.github.fmueller.jarvis.ai

import com.github.fmueller.jarvis.conversation.Conversation
import dev.langchain4j.model.ollama.OllamaStreamingChatModel
import dev.langchain4j.service.AiServices
import dev.langchain4j.service.TokenStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
Expand All @@ -12,6 +15,7 @@ import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
import java.util.concurrent.CompletableFuture

@Serializable
private data class ChatMessage(val role: String, val content: String)
Expand All @@ -28,31 +32,61 @@ private data class ChatResponse(val message: ChatMessage)

object OllamaService {

// TODO should I add something about putting the language identifier in the code block?
private val systemMessage = """
You are Jarvis.
You are a helpful coding assistant on the level of an expert software developer.
You ask clarifying questions to understand the user's problem if you don't have enough information.
You are running in the IDE of the user.
You have access to code the user is working on.
You format responses in Markdown.
You use paragraphs, lists, and code blocks to make your responses more readable.
""".trimIndent()

private val client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build()

suspend fun chat(conversation: Conversation): String = withContext(Dispatchers.IO) {
private interface Assistant {

fun chat(message: String): TokenStream
}

private val assistant = AiServices
.builder(Assistant::class.java)
.streamingChatLanguageModel(
OllamaStreamingChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1")
.build()
)
.systemMessageProvider { chatMemoryId -> systemMessage }
.build()

suspend fun chatLangChain4J(conversation: Conversation): String = withContext(Dispatchers.IO) {

Check warning on line 66 in src/main/kotlin/com/github/fmueller/jarvis/ai/OllamaService.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "chatLangChain4J" is never used
// TODO check if model is available
// TODO if not, download model

// TODO migration to LangChain4J: change to Kotlin Coroutines
// TODO migration to LangChain4J: add timeout handling
// TODO migration to LangChain4J: research how token limits work and context windows
// TODO migration to LangChain4J: research how chat memory is configured
val future = CompletableFuture<String>()
assistant
.chat(conversation.getLastUserMessage()?.content ?: "Tell me that there was not message provided.")
.onNext { update -> conversation.addToMessageBeingGenerated(update) }
.onComplete { response -> future.complete(response.content().text()) }
.onError { error -> future.complete("Error: ${error.message}") }
.start()
future.get()
}

suspend fun chat(conversation: Conversation): String = withContext(Dispatchers.IO) {
try {
val chatRequest = ChatRequest(
"llama3.1",
listOf(
ChatMessage(
"system",
// TODO is it better to reference the assistant by name or role?
// TODO should I add something about putting the language identifier in the code block?
"""
You are Jarvis.
You are a helpful coding assistant on the level of an expert software developer.
You ask clarifying questions to understand the user's problem if you don't have enough information.
You are running in the IDE of the user.
You have access to code the user is working on.
You format responses in Markdown.
You use paragraphs, lists, and code blocks to make your responses more readable.
""".trimIndent()
)
ChatMessage("system", systemMessage)
) + conversation.messages.map { ChatMessage(it.role.toString(), it.asMarkdown()) },
true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class Conversation : Disposable {
return SlashCommandParser.parse(message.content).run(this)
}

fun getLastUserMessage(): Message? = _messages.lastOrNull { it.role == Role.USER }

fun addToMessageBeingGenerated(text: String) {
val old = _messageBeingGenerated.toString()
_messageBeingGenerated.append(text)
Expand Down

0 comments on commit bf333d3

Please sign in to comment.