From 48134d1f3d9e3af067908adeca74eb59db5328c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9?= Date: Fri, 15 Sep 2023 01:52:34 +0300 Subject: [PATCH] renewed server side according to spec added implementation to get a models list adjusted some namings improved loggers --- src/main/kotlin/OSSLLMCompletionProvider.kt | 21 +++---- .../github/bzz/intellij/actions/TestAction.kt | 7 ++- .../bzz/intellij/models/AvailableModels.kt | 21 ++++++- .../github/bzz/intellij/requests/JsonData.kt | 34 ++++++++++++ .../github/bzz/intellij/requests/Requester.kt | 55 ++++++++++++++----- .../bzz/intellij/requests/ServerResponse.kt | 8 --- ...ndowFactory.kt => LLMToolWindowFactory.kt} | 0 7 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 src/main/kotlin/com/github/bzz/intellij/requests/JsonData.kt delete mode 100644 src/main/kotlin/com/github/bzz/intellij/requests/ServerResponse.kt rename src/main/kotlin/com/github/bzz/intellij/toolWindow/{MyToolWindowFactory.kt => LLMToolWindowFactory.kt} (100%) diff --git a/src/main/kotlin/OSSLLMCompletionProvider.kt b/src/main/kotlin/OSSLLMCompletionProvider.kt index bffa212..84dc528 100644 --- a/src/main/kotlin/OSSLLMCompletionProvider.kt +++ b/src/main/kotlin/OSSLLMCompletionProvider.kt @@ -4,28 +4,29 @@ import com.github.bzz.intellij.requests.Requester import com.intellij.codeInsight.inline.completion.InlineCompletionElement import com.intellij.codeInsight.inline.completion.InlineCompletionProvider import com.intellij.codeInsight.inline.completion.InlineCompletionRequest -import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.event.DocumentEvent import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental class OSSLLMCompletionProvider : InlineCompletionProvider { - override suspend fun getProposals(request: InlineCompletionRequest): List { + private val logger = Logger.getInstance("(when autocompleting)") + + override suspend fun getProposals(request: InlineCompletionRequest): List = try { val caretPosition = request.startOffset - val proposal = Requester.getModelSuggestions( + val proposals = Requester.getModelSuggestions( request.document.text.substring(0, caretPosition) - ).text ?: return emptyList() - return listOf(InlineCompletionElement(proposal)) + ) + proposals.results.map { InlineCompletionElement(it.text) } } catch (knownException: Requester.RequesterException) { - thisLogger().warn(knownException.message) - return emptyList() + logger.warn(knownException.message) + emptyList() } catch (exception: Exception) { - thisLogger().warn("Unknown exception. Message: ${exception.message}") - return emptyList() + logger.warn("Unknown exception. Message: ${exception.message}") + emptyList() } - } override fun isEnabled(event: DocumentEvent): Boolean { return true diff --git a/src/main/kotlin/com/github/bzz/intellij/actions/TestAction.kt b/src/main/kotlin/com/github/bzz/intellij/actions/TestAction.kt index 63adb83..7f6ad9c 100644 --- a/src/main/kotlin/com/github/bzz/intellij/actions/TestAction.kt +++ b/src/main/kotlin/com/github/bzz/intellij/actions/TestAction.kt @@ -13,6 +13,9 @@ class TestAction: AnAction() { val editor = event.getRequiredData(CommonDataKeys.EDITOR) val caretPosition = editor.caretModel.primaryCaret.selectionStart - logger.warn(Requester.getModelSuggestions(editor.document.text).text?.substring(0, caretPosition)) + logger.warn( + Requester.getModelSuggestions(editor.document.text.substring(0, caretPosition)). + results.firstOrNull()?.text ?: "---" + ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/bzz/intellij/models/AvailableModels.kt b/src/main/kotlin/com/github/bzz/intellij/models/AvailableModels.kt index 3c9efdb..eb2c87f 100644 --- a/src/main/kotlin/com/github/bzz/intellij/models/AvailableModels.kt +++ b/src/main/kotlin/com/github/bzz/intellij/models/AvailableModels.kt @@ -1,5 +1,7 @@ package com.github.bzz.intellij.com.github.bzz.intellij.models +import com.github.bzz.intellij.requests.Requester +import com.intellij.openapi.diagnostic.Logger import java.util.concurrent.atomic.AtomicInteger @@ -7,5 +9,22 @@ object AvailableModels { val currentModelIndex = AtomicInteger(0) - val modelsList: List = listOf("proxy-model", "other proxy-model") + private val logger = Logger.getInstance("(getting available models)") + + private fun getAvailableModels(): List = + try { + Requester.getAvailableModels().models.map { it.modelName } + } catch (knownException: Requester.RequesterException) { + logger.warn(knownException.message) + emptyList() + } catch (otherException: Requester.RequesterException) { + logger.warn("Unknown exception. Message: ${otherException.message}") + emptyList() + } + + val modelsList: List = getAvailableModels() + + fun currentModel() : String? { + return modelsList.getOrNull(currentModelIndex.get()) + } } diff --git a/src/main/kotlin/com/github/bzz/intellij/requests/JsonData.kt b/src/main/kotlin/com/github/bzz/intellij/requests/JsonData.kt new file mode 100644 index 0000000..9ce3f22 --- /dev/null +++ b/src/main/kotlin/com/github/bzz/intellij/requests/JsonData.kt @@ -0,0 +1,34 @@ +package com.github.bzz.intellij.com.github.bzz.intellij.requests + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + + +@Serializable +data class ServerProposal ( + val text: String +) + +@Serializable +data class ProposalsResponse ( + val results: List +) + +@Serializable +data class LLMModel ( + @SerializedName("model_name") + val modelName: String +) + +@Serializable +data class ModelsResponse( + val models: List +) + +@Serializable +data class ProposalsQuery( + val model: String, + val prompt: String, + @SerializedName("max_new_tokens") + val maxNewTokens: Int +) diff --git a/src/main/kotlin/com/github/bzz/intellij/requests/Requester.kt b/src/main/kotlin/com/github/bzz/intellij/requests/Requester.kt index 6346104..b0e8ca2 100644 --- a/src/main/kotlin/com/github/bzz/intellij/requests/Requester.kt +++ b/src/main/kotlin/com/github/bzz/intellij/requests/Requester.kt @@ -1,7 +1,11 @@ package com.github.bzz.intellij.requests -import com.github.bzz.intellij.com.github.bzz.intellij.requests.ServerResponse +import com.github.bzz.intellij.com.github.bzz.intellij.models.AvailableModels +import com.github.bzz.intellij.com.github.bzz.intellij.requests.ModelsResponse +import com.github.bzz.intellij.com.github.bzz.intellij.requests.ProposalsQuery +import com.github.bzz.intellij.com.github.bzz.intellij.requests.ProposalsResponse import com.google.gson.Gson +import com.google.gson.JsonSyntaxException import java.io.IOException import java.net.URI import java.net.http.HttpClient @@ -11,30 +15,56 @@ import java.net.http.HttpTimeoutException object Requester { + + const val MAX_TOKEN_LENGTH = 10 open class RequesterException(message: String): RuntimeException("RequesterException: $message") private class NoListenerRequesterException(ip: String, port: Int): RequesterException("Probable cause: server $ip is not running or listening at port $port.") - private class TimeoutRequesterException(): - RequesterException("Probable cause: new model is being downloaded currently.") + private class TimeoutRequesterException: + RequesterException("Probable cause: poor connection.") private class GeneralRequesterException(ip: String, port: Int, httpStatus: Int): RequesterException("HTTP Request to $ip:$port failed. Failure status code: $httpStatus") - private class LocationMissingException(ip: String): - RequesterException("Redirect response from $ip is missing Location header") + private class JsonRequesterException(ip: String, port: Int, answer: String?): + RequesterException("Response from $ip:$port has incompatible json: $answer") + + private class NoLoadedLLMException: + RequesterException("No LLM is chosen due to the absence of any.") private val client = HttpClient.newBuilder().build() - fun getModelSuggestions(context: String, ip: String = "localhost", port: Int = 8000): ServerResponse { + fun getAvailableModels(ip: String = "localhost", port: Int = 8000): ModelsResponse { + val request = HttpRequest.newBuilder() + .uri(URI.create("http://$ip:$port")) + .GET() + .timeout(java.time.Duration.ofSeconds(10)) + .build() + + return serverQuery(ip, port, request, ModelsResponse::class.java) + } + + fun getModelSuggestions(context: String, ip: String = "localhost", port: Int = 8000): ProposalsResponse { + val queryParams = ProposalsQuery( + model = AvailableModels.currentModel() ?: throw NoLoadedLLMException(), + prompt = context, + maxNewTokens = MAX_TOKEN_LENGTH + ) + val json = Gson().toJson(queryParams) val request = HttpRequest.newBuilder() .uri(URI.create("http://$ip:$port")) - .POST(HttpRequest.BodyPublishers.ofString(context)) + .POST(HttpRequest.BodyPublishers.ofString(json)) + .header("Content-type", "application/json") .timeout(java.time.Duration.ofSeconds(10)) .build() + return serverQuery(ip, port, request, ProposalsResponse::class.java) + } + + private fun serverQuery(ip: String, port: Int, request: HttpRequest, serializableClass: Class): T { val response = try { client.send(request, HttpResponse.BodyHandlers.ofString()) } catch(toe: HttpTimeoutException) { @@ -44,13 +74,10 @@ object Requester { } return when (response.statusCode()) { - 200 -> Gson().fromJson(response.body(), ServerResponse::class.java) - 301, 302, 303, 307 -> { - val newLocation = response.headers().firstValue("Location") - .orElseThrow { - LocationMissingException(ip) - } - getModelSuggestions(context, newLocation) + 200 -> try { + Gson().fromJson(response.body(), serializableClass) + } catch(jsonException: JsonSyntaxException) { + throw JsonRequesterException(ip, port, response.body()) } else -> throw GeneralRequesterException(ip, port, response.statusCode()) } diff --git a/src/main/kotlin/com/github/bzz/intellij/requests/ServerResponse.kt b/src/main/kotlin/com/github/bzz/intellij/requests/ServerResponse.kt deleted file mode 100644 index 9058642..0000000 --- a/src/main/kotlin/com/github/bzz/intellij/requests/ServerResponse.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.bzz.intellij.com.github.bzz.intellij.requests - -import kotlinx.serialization.Serializable - -@Serializable -data class ServerResponse ( - val text: String? -) \ No newline at end of file diff --git a/src/main/kotlin/com/github/bzz/intellij/toolWindow/MyToolWindowFactory.kt b/src/main/kotlin/com/github/bzz/intellij/toolWindow/LLMToolWindowFactory.kt similarity index 100% rename from src/main/kotlin/com/github/bzz/intellij/toolWindow/MyToolWindowFactory.kt rename to src/main/kotlin/com/github/bzz/intellij/toolWindow/LLMToolWindowFactory.kt