chat ui
This commit is contained in:
+19
-25
@@ -1,31 +1,40 @@
|
||||
package ru.shadowsparky.chat.backend.data
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.auth.Auth
|
||||
import io.ktor.client.plugins.auth.providers.BearerAuthProvider
|
||||
import io.ktor.client.plugins.auth.providers.BearerTokens
|
||||
import io.ktor.client.plugins.auth.providers.bearer
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.annotation.Factory
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.chat.backend.domain.ChatService
|
||||
import ru.shadowsparky.chat.domain.Message
|
||||
import ru.shadowsparky.http.domain.HttpClientFactory
|
||||
import java.util.UUID
|
||||
|
||||
@Factory
|
||||
@Single
|
||||
class GigaChatService(
|
||||
private val tokenManager: GigaChatTokenManager,
|
||||
private val json: Json
|
||||
private val httpClientFactory: HttpClientFactory
|
||||
) : ChatService {
|
||||
private val httpClient by lazy { createGigaChatHttpClient() }
|
||||
private val httpClient by lazy {
|
||||
httpClientFactory.create(
|
||||
BearerAuthProvider(
|
||||
refreshTokens = {
|
||||
val newToken = tokenManager.forceRefreshToken()
|
||||
BearerTokens(newToken, null)
|
||||
},
|
||||
loadTokens = {
|
||||
BearerTokens(tokenManager.getToken(), null)
|
||||
},
|
||||
realm = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getCompletion(userId: Long, messages: List<Message>): String {
|
||||
val clientId = "vbox-$userId"
|
||||
@@ -44,21 +53,6 @@ class GigaChatService(
|
||||
?: error("unexpected response $response")
|
||||
}
|
||||
|
||||
private fun createGigaChatHttpClient(): HttpClient {
|
||||
return HttpClient {
|
||||
install(ContentNegotiation) { json(json) }
|
||||
install(Auth) {
|
||||
bearer {
|
||||
loadTokens { BearerTokens(tokenManager.getToken(), "") }
|
||||
refreshTokens {
|
||||
val newToken = tokenManager.forceRefreshToken()
|
||||
BearerTokens(newToken, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class GigaChatRequest(
|
||||
val model: String = "GigaChat-2",
|
||||
|
||||
+27
-8
@@ -1,13 +1,13 @@
|
||||
package ru.shadowsparky.chat.backend.data
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.auth.providers.BearerAuthProvider
|
||||
import io.ktor.client.plugins.auth.providers.BearerTokens
|
||||
import io.ktor.client.request.forms.FormDataContent
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.Parameters
|
||||
import io.ktor.http.contentType
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@@ -17,16 +17,35 @@ import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.backend.data.EnvFetcher
|
||||
import ru.shadowsparky.http.data.RefreshTokenRunner
|
||||
import ru.shadowsparky.http.domain.BadRequestException
|
||||
import ru.shadowsparky.http.domain.HttpClientFactory
|
||||
import java.util.UUID
|
||||
|
||||
@Single
|
||||
class GigaChatTokenManager(
|
||||
private val authClient: HttpClient,
|
||||
private val json: Json,
|
||||
private val envFetcher: EnvFetcher
|
||||
private val envFetcher: EnvFetcher,
|
||||
private val httpClientFactory: HttpClientFactory,
|
||||
private val refreshTokenRunner: RefreshTokenRunner
|
||||
) {
|
||||
private val scope: String = "GIGACHAT_API_PERS"
|
||||
|
||||
private val authClient by lazy {
|
||||
httpClientFactory.create(
|
||||
BearerAuthProvider(
|
||||
refreshTokens = { null },
|
||||
loadTokens = {
|
||||
BearerTokens(
|
||||
envFetcher.get("GIGA_CHAT_AUTH_TOKEN").ifEmpty { error("auth token not provided") },
|
||||
null
|
||||
)
|
||||
},
|
||||
realm = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val mutex = Mutex()
|
||||
private var cachedToken: String? = null
|
||||
|
||||
@@ -34,14 +53,14 @@ class GigaChatTokenManager(
|
||||
cachedToken ?: fetchNewToken()
|
||||
}
|
||||
|
||||
suspend fun forceRefreshToken(): String = mutex.withLock {
|
||||
fetchNewToken()
|
||||
suspend fun forceRefreshToken(): String {
|
||||
return refreshTokenRunner.runOrJoin {
|
||||
BearerTokens(fetchNewToken(), null)
|
||||
}?.accessToken ?: throw BadRequestException("Unable to refresh access token")
|
||||
}
|
||||
|
||||
private suspend fun fetchNewToken(): String {
|
||||
val auth = envFetcher.get("GIGA_CHAT_AUTH_TOKEN").ifEmpty { error("auth token not provided") }
|
||||
val responseString: String = authClient.post("https://ngw.devices.sberbank.ru:9443/api/v2/oauth") {
|
||||
header(HttpHeaders.Authorization, auth)
|
||||
header("RqUID", UUID.randomUUID().toString())
|
||||
contentType(ContentType.Application.FormUrlEncoded)
|
||||
setBody(FormDataContent(Parameters.build {
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@ package ru.shadowsparky.chat.backend.domain
|
||||
import ru.shadowsparky.chat.domain.Message
|
||||
|
||||
interface MessageStorage {
|
||||
suspend fun query(userId: Long, limit: Int): List<Message>
|
||||
suspend fun query(userId: Long, afterId: Long?, limit: Int): List<Message>
|
||||
suspend fun insert(userId: Long, message: Message): Long
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ class ProcessUserMessageUseCase(
|
||||
private val chatService: ChatService
|
||||
) {
|
||||
suspend fun execute(userId: Long): Message {
|
||||
val history = storage.query(userId, limit = 10).toList()
|
||||
val history = storage.query(userId, null, limit = 10).toList()
|
||||
val sortedHistory = history.sortedBy { it.timestamp }
|
||||
val systemMessage = Message(
|
||||
role = ChatRoles.SYSTEM,
|
||||
|
||||
Reference in New Issue
Block a user