> ## Documentation Index
> Fetch the complete documentation index at: https://docs.freeplay.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI Function Calls

> Implement function calling with OpenAI and record tool interactions to Freeplay.

### 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 calling `get_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

<CodeGroup>
  ```python Python theme={null}
  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
      )
  )
  ```

  ```javascript Node theme={null}
  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
  });
  ```

  ```java Kotlin theme={null}
  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()
      }
  }
  ```
</CodeGroup>
