Initial commit
This commit is contained in:
commit
cda6eb7ce0
680 changed files with 75081 additions and 0 deletions
141
app/src/main/java/com/mistral/chat/api/MistralClient.kt
Normal file
141
app/src/main/java/com/mistral/chat/api/MistralClient.kt
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package com.mistral.chat.api
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.mistral.chat.data.Message
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MistralClient(private val apiKey: String) {
|
||||
|
||||
private val client = OkHttpClient.Builder()
|
||||
.connectTimeout(60, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
private val gson = Gson()
|
||||
private val jsonMediaType = "application/json".toMediaType()
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL = "https://api.mistral.ai/v1"
|
||||
|
||||
val AVAILABLE_MODELS = listOf(
|
||||
"mistral-small-latest" to "Mistral Small",
|
||||
"mistral-medium-latest" to "Mistral Medium",
|
||||
"mistral-large-latest" to "Mistral Large",
|
||||
"codestral-latest" to "Codestral",
|
||||
"pixtral-large-latest" to "Pixtral Large"
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getModels(): Result<List<Pair<String, String>>> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_URL/models")
|
||||
.addHeader("Authorization", "Bearer $apiKey")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
return@withContext Result.failure(Exception("API error: ${response.code}"))
|
||||
}
|
||||
|
||||
val responseBody = response.body?.string() ?: ""
|
||||
val responseJson = gson.fromJson(responseBody, JsonObject::class.java)
|
||||
|
||||
val models = responseJson
|
||||
.getAsJsonArray("data")
|
||||
?.mapNotNull { obj ->
|
||||
val jsonObj = obj.asJsonObject
|
||||
val id = jsonObj.get("id")?.asString
|
||||
val created = jsonObj.get("created")?.asLong ?: 0L
|
||||
if (id != null && created > 0 && id.endsWith("-latest")) {
|
||||
val displayName = id
|
||||
.replace("-latest", "")
|
||||
.replace("-", " ")
|
||||
.split(" ")
|
||||
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
id to displayName
|
||||
} else null
|
||||
} ?: emptyList()
|
||||
|
||||
Result.success(models)
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun chat(
|
||||
model: String,
|
||||
messages: List<Message>,
|
||||
onChunk: ((String) -> Unit)? = null
|
||||
): Result<Pair<String, String>> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val jsonObject = JsonObject()
|
||||
jsonObject.addProperty("model", model)
|
||||
jsonObject.addProperty("temperature", 0.7)
|
||||
jsonObject.addProperty("stream", onChunk != null)
|
||||
|
||||
val messagesArray = JsonArray()
|
||||
messages.forEach { msg ->
|
||||
val msgObj = JsonObject()
|
||||
msgObj.addProperty("role", if (msg.isUser) "user" else "assistant")
|
||||
msgObj.addProperty("content", msg.content)
|
||||
messagesArray.add(msgObj)
|
||||
}
|
||||
jsonObject.add("messages", messagesArray)
|
||||
|
||||
val json = gson.toJson(jsonObject)
|
||||
val body = json.toRequestBody(jsonMediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_URL/chat/completions")
|
||||
.addHeader("Authorization", "Bearer $apiKey")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
val errorBody = response.body?.string() ?: "Unknown error"
|
||||
return@withContext Result.failure(Exception("API error: ${response.code} - $errorBody"))
|
||||
}
|
||||
|
||||
val responseBody = response.body?.string() ?: ""
|
||||
|
||||
if (onChunk != null) {
|
||||
onChunk(responseBody)
|
||||
}
|
||||
|
||||
val responseJson = gson.fromJson(responseBody, JsonObject::class.java)
|
||||
|
||||
val choices = responseJson.getAsJsonArray("choices")
|
||||
if (choices == null || choices.size() == 0) {
|
||||
return@withContext Result.failure(Exception("No response from API"))
|
||||
}
|
||||
|
||||
val content = choices
|
||||
.get(0)
|
||||
?.asJsonObject
|
||||
?.getAsJsonObject("message")
|
||||
?.get("content")
|
||||
?.asString ?: ""
|
||||
|
||||
val usedModel = responseJson.get("model")?.asString ?: model
|
||||
|
||||
Result.success(content to usedModel)
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue