@@ -281,6 +281,10 @@ class ScreenCaptureService : Service() {
281281 val result = callVercelApi(modelName, apiKey, chatHistory, inputContent)
282282 responseText = result.first
283283 errorMessage = result.second
284+ } else if (apiProvider == ApiProvider .MISTRAL ) {
285+ val result = callMistralApi(modelName, apiKey, chatHistory, inputContent)
286+ responseText = result.first
287+ errorMessage = result.second
284288 } else {
285289 val generativeModel = GenerativeModel (
286290 modelName = modelName,
@@ -829,3 +833,113 @@ private suspend fun callVercelApi(modelName: String, apiKey: String, chatHistory
829833
830834 return Pair (responseText, errorMessage)
831835}
836+
837+ // Data classes for Mistral API in Service
838+ @Serializable
839+ data class ServiceMistralRequest (
840+ val model : String ,
841+ val messages : List <ServiceMistralMessage >,
842+ val max_tokens : Int = 4096 ,
843+ val temperature : Double = 0.7 ,
844+ val top_p : Double = 1.0 ,
845+ val stream : Boolean = false
846+ )
847+
848+ @Serializable
849+ data class ServiceMistralMessage (
850+ val role : String ,
851+ val content : List <ServiceMistralContent >
852+ )
853+
854+ @Serializable
855+ @JsonClassDiscriminator(" type" )
856+ sealed class ServiceMistralContent
857+
858+ @Serializable
859+ @SerialName(" text" )
860+ data class ServiceMistralTextContent (@SerialName(" text" ) val text : String ) : ServiceMistralContent()
861+
862+ @Serializable
863+ @SerialName(" image_url" )
864+ data class ServiceMistralImageContent (@SerialName(" image_url" ) val imageUrl : ServiceMistralImageUrl ) : ServiceMistralContent()
865+
866+ @Serializable
867+ data class ServiceMistralImageUrl (val url : String )
868+
869+ @Serializable
870+ data class ServiceMistralResponse (
871+ val choices : List <ServiceMistralChoice >
872+ )
873+
874+ @Serializable
875+ data class ServiceMistralChoice (
876+ val message : ServiceMistralResponseMessage
877+ )
878+
879+ @Serializable
880+ data class ServiceMistralResponseMessage (
881+ val role : String ,
882+ val content : String
883+ )
884+
885+ private suspend fun callMistralApi (modelName : String , apiKey : String , chatHistory : List <Content >, inputContent : Content ): Pair <String ?, String ?> {
886+ var responseText: String? = null
887+ var errorMessage: String? = null
888+
889+ val json = Json {
890+ serializersModule = SerializersModule {
891+ polymorphic(ServiceMistralContent ::class ) {
892+ subclass(ServiceMistralTextContent ::class , ServiceMistralTextContent .serializer())
893+ subclass(ServiceMistralImageContent ::class , ServiceMistralImageContent .serializer())
894+ }
895+ }
896+ ignoreUnknownKeys = true
897+ }
898+
899+ try {
900+ val messages = (chatHistory + inputContent).map { content ->
901+ val parts = content.parts.map { part ->
902+ when (part) {
903+ is TextPart -> ServiceMistralTextContent (text = part.text)
904+ is ImagePart -> ServiceMistralImageContent (imageUrl = ServiceMistralImageUrl (url = part.image.toBase64()))
905+ else -> ServiceMistralTextContent (text = " " )
906+ }
907+ }
908+ ServiceMistralMessage (role = if (content.role == " user" ) " user" else " assistant" , content = parts)
909+ }
910+
911+ val requestBody = ServiceMistralRequest (
912+ model = modelName,
913+ messages = messages
914+ )
915+
916+ val client = OkHttpClient ()
917+ val mediaType = " application/json" .toMediaType()
918+ val jsonBody = json.encodeToString(ServiceMistralRequest .serializer(), requestBody)
919+
920+ val request = Request .Builder ()
921+ .url(" https://api.mistral.ai/v1/chat/completions" )
922+ .post(jsonBody.toRequestBody(mediaType))
923+ .addHeader(" Content-Type" , " application/json" )
924+ .addHeader(" Authorization" , " Bearer $apiKey " )
925+ .build()
926+
927+ client.newCall(request).execute().use { response ->
928+ if (! response.isSuccessful) {
929+ errorMessage = " Unexpected code ${response.code} - ${response.body?.string()} "
930+ } else {
931+ val responseBody = response.body?.string()
932+ if (responseBody != null ) {
933+ val mistralResponse = json.decodeFromString(ServiceMistralResponse .serializer(), responseBody)
934+ responseText = mistralResponse.choices.firstOrNull()?.message?.content ? : " No response from model"
935+ } else {
936+ errorMessage = " Empty response body"
937+ }
938+ }
939+ }
940+ } catch (e: Exception ) {
941+ errorMessage = e.localizedMessage ? : " Mistral API call failed"
942+ }
943+
944+ return Pair (responseText, errorMessage)
945+ }
0 commit comments