1. Setup clients
Initialize Freeplay and OpenAI client SDKs.2. Fetch prompt from Freeplay
Pull in the formatted prompt with Freeplay. The prompt contains tools schema that’ll we’ll pass down below.3. Call OpenAI with the tools
When creating a new completion, pass in the tools schema from the prompt we fetched.4. Handle tool call
When LLM responds back with a tool call, call the external function in your service. As an example here, we are callingget_temperature function
5. Record tool call and schema
Pass in the schema and completion response to capture the tool call and its associated schema.Examples
import os
import time
import json
from openai import OpenAI
from freeplay import Freeplay, RecordPayload, CallInfo
# A mock function that gets temperature for a location
def get_temperature(location: str) -> float:
return 72.5
fp_client = Freeplay(
freeplay_api_key=os.environ['FREEPLAY_API_KEY'],
api_base=f"{os.environ['FREEPLAY_API_URL']}/api"
)
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
project_id=os.environ['FREEPLAY_PROJECT_ID']
input_variables = {'location': "Boulder, CO"}
formatted_prompt = fp_client.prompts.get_formatted(
project_id=project_id,
template_name='my-openai-prompt',
environment='latest',
variables=input_variables
)
start = time.time()
completion = client.chat.completions.create(
messages=formatted_prompt.llm_prompt,
model=formatted_prompt.prompt_info.model,
tools=formatted_prompt.tool_schema,
**formatted_prompt.prompt_info.model_parameters
)
end = time.time()
# Append the completion to list of messages
messages = formatted_prompt.all_messages(completion.choices[0].message)
if completion.choices[0].message.tool_calls:
for tool_call in completion.choices[0].message.tool_calls:
if tool_call.function.name == "weather_of_location":
args = json.loads(tool_call.function.arguments)
temperature = get_temperature(args["location"])
tool_response_message = {
"tool_call_id": tool_call.id,
"role": "tool",
"content": str(temperature),
}
messages.append(tool_response_message)
session = fp_client.sessions.create()
fp_client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=messages,
session_info=session.session_info,
inputs=input_variables,
prompt_version_info=formatted_prompt.prompt_info,
call_info=CallInfo.from_prompt_info(formatted_prompt.prompt_info, start, end),
tool_schema=formatted_prompt.tool_schema
)
)
import OpenAI from "openai";
import Freeplay, { getSessionInfo, getCallInfo } from "freeplay";
// A mock function that gets temperature for a location
function getTemperature(location) {
return 72.5;
}
const projectId = process.env.FREEPLAY_PROJECT_ID;
const fp_client = new Freeplay({
freeplayApiKey: process.env.FREEPLAY_API_KEY,
baseUrl: `${process.env.FREEPLAY_API_URL}/api`,
});
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const inputVariables = { location: "Boulder, CO" };
const formattedPrompt = await fp_client.prompts.getFormatted({
projectId,
templateName: "my-openai-prompt",
environment: "latest",
variables: inputVariables,
});
const start = new Date();
const completion = await client.chat.completions.create({
messages: formattedPrompt.llmPrompt,
model: formattedPrompt.promptInfo.model,
tools: formattedPrompt.toolSchema,
...formattedPrompt.promptInfo.modelParameters,
});
const end = new Date();
// Append the completion to list of messages
const messages = formattedPrompt.allMessages(completion.choices[0].message);
if (completion.choices[0].message.tool_calls) {
for (const toolCall of completion.choices[0].message.tool_calls) {
if (toolCall.function.name === "weather_of_location") {
const args = JSON.parse(toolCall.function.arguments);
const temperature = getTemperature(args.location);
const toolResponseMessage = {
tool_call_id: toolCall.id,
role: "tool",
content: temperature.toString(),
};
messages.push(toolResponseMessage);
}
}
}
const session = fp_client.sessions.create();
await fp_client.recordings.create({
projectId,
allMessages: messages,
sessionInfo: getSessionInfo(session),
inputs: inputVariables,
promptVersionInfo: formattedPrompt.promptInfo,
callInfo: getCallInfo(formattedPrompt.promptInfo, start, end),
toolSchema: formattedPrompt.toolSchema
});
package ai.freeplay.example.kotlin
import ai.freeplay.client.thin.Freeplay
import ai.freeplay.client.thin.resources.prompts.ChatMessage
import ai.freeplay.client.thin.resources.recordings.CallInfo
import ai.freeplay.client.thin.resources.recordings.RecordInfo
import com.fasterxml.jackson.databind.ObjectMapper
import ai.freeplay.example.java.ThinExampleUtils.callOpenAIWithTools
object OpenAIToolsExample {
private val objectMapper = ObjectMapper()
// Mock weather function
private fun getTemperature(location: String): Double = 72.5
@JvmStatic
fun main(args: Array<String>) {
val freeplayApiKey = System.getenv("FREEPLAY_API_KEY")
val projectId = System.getenv("FREEPLAY_PROJECT_ID")
val apiRoot = System.getenv("FREEPLAY_API_URL")
val baseUrl = "${apiRoot}/api"
val openaiApiKey = System.getenv("OPENAI_API_KEY")
val fpClient = Freeplay(
Freeplay.Config()
.freeplayAPIKey(freeplayApiKey)
.baseUrl(baseUrl)
)
val variables = mapOf("location" to "Boulder, CO")
fpClient.prompts()
.getFormatted<List<ChatMessage>>(
projectId,
"my-openai-prompt",
"latest",
variables,
null
).thenCompose { formattedPrompt ->
val startTime = System.currentTimeMillis()
callOpenAIWithTools(
objectMapper,
openaiApiKey,
formattedPrompt.promptInfo.model,
formattedPrompt.promptInfo.modelParameters,
formattedPrompt.formattedPrompt,
formattedPrompt.toolSchema
).thenApply { response ->
Triple(formattedPrompt, response, startTime)
}
}.thenCompose { (formattedPrompt, response, startTime) ->
try {
val bodyNode = objectMapper.readTree(response.body())
val choicesNode = bodyNode["choices"]
val messageNode = choicesNode[0]["message"]
val message = objectMapper.convertValue(messageNode, Object::class.java)
val allMessages = formattedPrompt.allMessages(message).toMutableList()
// Handle tool calls
val toolCalls = messageNode["tool_calls"]
if (toolCalls != null && toolCalls.isArray) {
toolCalls.forEach { toolCall ->
if ("weather_of_location" == toolCall["function"]["name"].asText()) {
val toolArgs = objectMapper.readTree(toolCall["function"]["arguments"].asText())
val temperature = getTemperature(toolArgs["location"].asText())
val toolResponse = mapOf(
"tool_call_id" to toolCall["id"].asText(),
"role" to "tool",
"content" to temperature.toString()
)
allMessages.add(toolResponse)
}
}
}
val callInfo = CallInfo.from(
formattedPrompt.promptInfo,
startTime,
System.currentTimeMillis()
)
val sessionInfo = fpClient.sessions().create().sessionInfo
fpClient.recordings().create(
RecordInfo(
projectId,
allMessages
).inputs(variables)
.sessionInfo(session.sessionInfo)
.promptVersionInfo(prompt.promptInfo)
.callInfo(callInfo)
.traceInfo(trace)
.toolSchema(formattedPrompt.toolSchema)
)
} catch (e: Exception) {
throw RuntimeException("Failed to process JSON response", e)
}
}
.exceptionally { exception ->
System.err.println("Error: ${exception.message}")
exception.printStackTrace()
null
}
.join()
}
}

