This guide is designed to demonstrate the interaction with OpenAI API using the Kotlin language to execute a chat completion request with function calls.
- The OpenAI Kotlin SDK installed.
- An API key from OpenAI.
We'll use the gpt-3.5-turbo-0613
model to create a chatbot that responds to a user's query about the weather in Boston. We'll send a chat completion request which includes a function currentWeather
that the model can call.
Firstly, we initialize an instance of the OpenAI class, providing it with the OpenAI API key.
val token = System.getenv("OPENAI_API_KEY")
val openAI = OpenAI(token)
Specify the model to use for the chat request.
val modelId = ModelId("gpt-3.5-turbo-0613")
Define a dummy function currentWeather
which the model might call. This function will return hardcoded weather information.
@Serializable
data class WeatherInfo(val location: String, val temperature: String, val unit: String, val forecast: List<String>)
/**
* Example dummy function hard coded to return the same weather
* In production, this could be your backend API or an external API
*/
fun currentWeather(location: String, unit: String): String {
val weatherInfo = WeatherInfo(location, "72", unit, listOf("sunny", "windy"))
return Json.encodeToString(weatherInfo)
}
Define the parameters for the function the model might call.
val params = Parameters.buildJsonObject {
put("type", "object")
putJsonObject("properties") {
putJsonObject("location") {
put("type", "string")
put("description", "The city and state, e.g. San Francisco, CA")
}
putJsonObject("unit") {
put("type", "string")
putJsonArray("enum") {
add("celsius")
add("fahrenheit")
}
}
}
putJsonArray("required") {
add("location")
}
}
Start with a user message inquiring about the weather in Boston.
val chatMessages = mutableListOf(
ChatMessage(
role = ChatRole.User,
content = "What's the weather like in Boston?"
)
)
Create a chat completion request that includes the model, messages, functions, and function call mode.
val request = chatCompletionRequest {
model = modelId
messages = chatMessages
functions {
function {
name = "currentWeather"
description = "Get the current weather in a given location"
parameters = params
}
}
functionCall = FunctionMode.Auto
}
Send the chat request and handle the response. If there's a function call in the response, execute it and send a second chat completion request.
val response = openAI.chatCompletion(request)
val message = response.choices.first().message ?: error("no response found!")
message.functionCall?.let { functionCall ->
val availableFunctions = mapOf("currentWeather" to ::currentWeather)
val functionToCall = availableFunctions[functionCall.name] ?: error("Function ${functionCall.name} not found")
val functionArgs = functionCall.argumentsAsJson() ?: error("arguments field is missing")
val functionResponse = functionToCall(
functionArgs.getValue("location").jsonPrimitive.content,
functionArgs["unit"]?.jsonPrimitive?.content ?: "fahrenheit"
)
chatMessages.add(
ChatMessage(
role = message.role,
content = message.content ?: "", // required to not be empty in this case
functionCall = message.functionCall
)
)
chatMessages.add(
ChatMessage(
role = ChatRole.Function,
name = functionCall.name,
content = functionResponse
)
)
val secondRequest = chatCompletionRequest {
model = modelId
messages = chatMessages
}
val secondResponse = openAI.chatCompletion(secondRequest)
println(secondResponse.choices.first().message?.content)
} ?: println(message.content)
- You should validate and handle the function calls made by the model carefully as the model could generate invalid JSON or hallucinate parameters.
- It's recommended to implement user confirmation flows before executing actions that could have an impact on the real world.
Below is a complete Kotlin example following the guide:
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.*
import com.aallam.openai.api.model.ModelId
import com.aallam.openai.client.OpenAI
import kotlinx.serialization.*
import kotlinx.serialization.json.*
suspend fun main() {
val token = System.getenv("OPENAI_API_KEY")
val openAI = OpenAI(token)
val modelId = ModelId("gpt-3.5-turbo-0613")
val chatMessages = mutableListOf(
ChatMessage(
role = ChatRole.User,
content = "What's the weather like in Boston?"
)
)
val params = Parameters.buildJsonObject {
put("type", "object")
putJsonObject("properties") {
putJsonObject("location") {
put("type", "string")
put("description", "The city and state, e.g. San Francisco, CA")
}
putJsonObject("unit") {
put("type", "string")
putJsonArray("enum") {
add("celsius")
add("fahrenheit")
}
}
}
putJsonArray("required") {
add("location")
}
}
val request = chatCompletionRequest {
model = modelId
messages = chatMessages
functions {
function {
name = "currentWeather"
description = "Get the current weather in a given location"
parameters = params
}
}
functionCall = FunctionMode.Auto
}
val response = openAI.chatCompletion(request)
val message = response.choices.first().message ?: error("no response found!")
message.functionCall?.let { functionCall ->
val availableFunctions = mapOf("currentWeather" to ::currentWeather)
val functionToCall = availableFunctions[functionCall.name] ?: error("Function ${functionCall.name} not found")
val functionArgs = functionCall.argumentsAsJson() ?: error("arguments field is missing")
val functionResponse = functionToCall(
functionArgs.getValue("location").jsonPrimitive.content,
functionArgs["unit"]?.jsonPrimitive?.content ?: "fahrenheit"
)
chatMessages.add(
ChatMessage(
role = message.role,
content = message.content ?: "",
functionCall = message.functionCall
)
)
chatMessages.add(
ChatMessage(
role = ChatRole.Function,
name = functionCall.name,
content = functionResponse
)
)
val secondRequest = chatCompletionRequest {
model = modelId
messages = chatMessages
}
val secondResponse = openAI.chatCompletion(secondRequest)
println(secondResponse.choices.first().message?.content)
} ?: println(message.content)
}
@Serializable
data class WeatherInfo(val location: String, val temperature: String, val unit: String, val forecast: List<String>)
fun currentWeather(location: String, unit: String): String {
val weatherInfo = WeatherInfo(location, "72", unit, listOf("sunny", "windy"))
return Json.encodeToString(weatherInfo)
}
This completes the guide for executing a chat completion request with a function call. Happy coding!