Update
This commit is contained in:
		
					parent
					
						
							
								ca615155d3
							
						
					
				
			
			
				commit
				
					
						9ccd1cd699
					
				
			
		
					 4 changed files with 34 additions and 18 deletions
				
			
		| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue