This commit is contained in:
Minecon724 2025-10-24 17:31:33 +02:00
commit 9ccd1cd699
Signed by untrusted user who does not match committer: m724
GPG key ID: A02E6E67AB961189
4 changed files with 34 additions and 18 deletions

View file

@ -7,6 +7,7 @@ import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.sse.SSE import io.ktor.client.plugins.sse.SSE
import io.ktor.client.plugins.sse.SSEBufferPolicy import io.ktor.client.plugins.sse.SSEBufferPolicy
import io.ktor.client.plugins.sse.deserialize
import io.ktor.client.plugins.sse.sse import io.ktor.client.plugins.sse.sse
import io.ktor.client.request.header import io.ktor.client.request.header
import io.ktor.client.request.setBody import io.ktor.client.request.setBody
@ -15,14 +16,11 @@ import io.ktor.http.HttpMethod
import io.ktor.http.contentType import io.ktor.http.contentType
import io.ktor.http.headers import io.ktor.http.headers
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.channels.awaitClose import io.ktor.sse.TypedServerSentEvent
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
class AiApi( class AiApi(
private val session: String private val session: String
@ -50,17 +48,16 @@ class AiApi(
method = HttpMethod.Post method = HttpMethod.Post
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(requestBody) setBody(requestBody)
},
deserialize = {
typeInfo, jsonString ->
val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!)
Json.decodeFromString(serializer, jsonString)!!
} }
) { ) {
block( block(
incoming.mapNotNull { incoming.mapNotNull { event: TypedServerSentEvent<String> ->
try { deserialize<StreamGptEvent.Completion>(event.data)
val streamGptEvent = Json.decodeFromString<StreamGptEvent.Completion>(it.data!!)
streamGptEvent
} catch (e: Exception) {
// TODO
null
}
} }
) )
} }

View file

@ -14,6 +14,7 @@ import com.jakewharton.mosaic.layout.size
import com.jakewharton.mosaic.layout.wrapContentSize import com.jakewharton.mosaic.layout.wrapContentSize
import com.jakewharton.mosaic.layout.wrapContentWidth import com.jakewharton.mosaic.layout.wrapContentWidth
import com.jakewharton.mosaic.modifier.Modifier import com.jakewharton.mosaic.modifier.Modifier
import com.jakewharton.mosaic.ui.Color
import com.jakewharton.mosaic.ui.Text import com.jakewharton.mosaic.ui.Text
import eu.m724.modifier.BorderedBoxWithTabs import eu.m724.modifier.BorderedBoxWithTabs
import eu.m724.modifier.RunTitle import eu.m724.modifier.RunTitle
@ -41,6 +42,11 @@ fun App(
}, },
subTitle = runs[selectedTabIndex].model ?: "" subTitle = runs[selectedTabIndex].model ?: ""
) { ) {
Text(
value = runs[selectedTabIndex].reasoningContent.wrap(90),
color = Color(200, 200, 200)
)
Text( Text(
value = runs[selectedTabIndex].content.wrap(90) value = runs[selectedTabIndex].content.wrap(90)
) )

View file

@ -28,10 +28,11 @@ class ViewModel {
var index = 0 var index = 0
_runs.update { _runs.update {
index = it.size index = it.size
it + Run(RunState.Queued, "") it + Run(RunState.Queued, "", "")
} }
var response = "" var reasoningContent = ""
var responseContent = ""
val timeSource = TimeSource.Monotonic val timeSource = TimeSource.Monotonic
val startMark by lazy { val startMark by lazy {
@ -43,6 +44,7 @@ class ViewModel {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
aiApi.requestCompletion( aiApi.requestCompletion(
requestBody = StreamGptRequestBody( requestBody = StreamGptRequestBody(
model = "free-model",
messages = listOf( messages = listOf(
ChatMessage( ChatMessage(
role = ChatMessage.Companion.Role.User, role = ChatMessage.Companion.Role.User,
@ -56,15 +58,25 @@ class ViewModel {
is StreamGptEvent.Completion -> { is StreamGptEvent.Completion -> {
val now = timeSource.markNow() val now = timeSource.markNow()
event.choices[0].delta.reasoning?.let { chunk ->
reasoningContent += chunk
tokens++
}
event.choices[0].delta.content?.let { chunk -> event.choices[0].delta.content?.let { chunk ->
response += chunk responseContent += chunk
tokens++ tokens++
} }
_runs.update { _runs.update {
it.toMutableList().apply { it.toMutableList().apply {
val tps = (tokens.toDouble() / (now - startMark).inWholeSeconds).toInt() val tps = (tokens.toDouble() / (now - startMark).inWholeSeconds).toInt()
this[index] = Run(RunState.InProgress(tps), response, model = event.model) this[index] = Run(
state = RunState.InProgress(tps),
reasoningContent = reasoningContent,
content = responseContent,
model = event.model
)
} }
} }
} }
@ -84,6 +96,7 @@ class ViewModel {
data class Run( data class Run(
val state: RunState, val state: RunState,
val reasoningContent: String,
val content: String, val content: String,
val model: String? = null val model: String? = null
) )

View file

@ -46,7 +46,7 @@ fun RunTitle(
} }
) )
if (state is RunState.InProgress) { if (state is RunState.InProgress && state.tokensPerSecond != Int.MAX_VALUE) {
Text( Text(
value = " ${state.tokensPerSecond}t/s", value = " ${state.tokensPerSecond}t/s",
color = Color(0, 120, 0) color = Color(0, 120, 0)