> ## 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.

# Managing Multi-Turn Chat History

> Manage conversation history for multi-turn chat applications with Freeplay.

### 1. Instantiate Clients

Instantiate Clients for Freeplay and your LLM Provider

### 2. Fetch Prompt Template

Fetch prompt template from Freeplay

### 3. Format Prompt

format prompt including input variables and history

### 4. Call LLM

Call your LLM provider, history will be merged into your prompt and ready to pass through

### 5. Record Interaction

Record the interaction to Freeplay

### 6. Manage history

Determine what to include in conversation history over each turn. Append the select messages to an array

## Examples

<CodeGroup>
  ```python Python theme={null}
  import json
  import os
  import time
  from copy import deepcopy
  from typing import Optional

  import boto3
  from anthropic import Anthropic, NotGiven
  from openai import OpenAI

  from freeplay import Freeplay, RecordPayload, CallInfo, SessionInfo, TraceInfo

  fp_client = Freeplay(
      freeplay_api_key=os.environ['FREEPLAY_API_KEY'],
      api_base=f"{os.environ['FREEPLAY_API_URL']}/api"
  )
  project_id = os.environ['FREEPLAY_PROJECT_ID']
  environment = 'dev'

  anthropic_client = Anthropic(
      api_key=os.environ.get("ANTHROPIC_API_KEY")
  )


  articles = [
      "george washington was the first president of the united states",
      "the sky is blue",
      "the earth is round",
      ""
  ]
  questions = [
      "who was the first president of the united states?",
      "what color is the sky?",
      "what shape is the earth?",
      "repeat the first question and answer"
  ]
  input_pairs = list(zip(articles, questions))

  template_prompt = fp_client.prompts.get(
      project_id=project_id,
      template_name='History-Basics',
      environment=environment
  )


  def call_and_record(
          project_id: str,
          template_name: str,
          env: str,
          history: list,
          input_variables: dict,
          session_info: SessionInfo,
          trace_info: Optional[TraceInfo] = None
  ) -> dict:
      formatted_prompt = fp_client.prompts.get_formatted(
          project_id=project_id,
          template_name=template_name,
          environment=env,
          variables=input_variables,
          history=history,
      )

      start = time.time()

      completion = anthropic_client.messages.create(
        system=formatted_prompt.system_content or NotGiven(),
        messages=formatted_prompt.llm_prompt,
        model=formatted_prompt.prompt_info.model,
        **formatted_prompt.prompt_info.model_parameters
      )
      end = time.time()
      llm_response = completion.content[0].text

      print("Completion: %s" % llm_response)

      assistant_response = {'role': 'assistant', 'content': llm_response}
      all_messages = formatted_prompt.all_messages(new_message=assistant_response)

      call_info = CallInfo.from_prompt_info(formatted_prompt.prompt_info, start, end)

      record_response = fp_client.recordings.create(
          RecordPayload(
              all_messages=all_messages,
              session_info=session_info,
              inputs=input_variables,
              prompt_info=formatted_prompt.prompt_info,
              call_info=call_info,
              trace_info=trace_info
          )
      )

      return {'completion_id': record_response.completion_id,
              'llm_response': assistant_response,
              "all_messages": all_messages}


  session = fp_client.sessions.create()

  history = []
  for inputs in input_pairs:
      input_vars = {'question': inputs[1], 'article': inputs[0]}
      record_response = call_and_record(
          project_id=project_id,
          template_name='History-QA',
          env=environment,
          history=history,
          input_variables=input_vars,
          session_info=session.session_info,
      )
      history = [msg for msg in record_response['all_messages'] if msg['role'] != 'system']


  ```

  ```javascript Node theme={null}
  import Freeplay, {getCallInfo, getSessionInfo} from "freeplay";
  import Anthropic from "@anthropic-ai/sdk";

  const projectId = process.env['FREEPLAY_PROJECT_ID'];
  const environment = 'dev';
  const templateName = "History-QA";

  const fpClient = new Freeplay({
      freeplayApiKey: process.env["FREEPLAY_API_KEY"],
      baseUrl: `${process.env["FREEPLAY_API_URL"]}/api`,
  });
  const anthropicClient = new Anthropic({apiKey: process.env['ANTHROPIC_API_KEY']})

  async function call(
      projectId,
      templateName,
      environment,
      input_variables,
      history,
      session_info,
      trace_info
  ){
      let formattedPrompt = await fpClient.prompts.getFormatted({
          projectId,
          templateName,
          environment,
          variables: input_variables,
          history: history
      });

      console.log("Prompt", formattedPrompt.llmPrompt);
      let start = new Date();
      const llmResponse = await anthropicClient.messages.create(
          {
              model: formattedPrompt.promptInfo.model,
              messages: formattedPrompt.llmPrompt,
              system: formattedPrompt.systemContent,
              ...formattedPrompt.promptInfo.modelParameters
          }
      );
      let end = new Date();

      const llmResponseText = llmResponse.content[0].text;

      let messages = formattedPrompt.allMessages(
          {
              content: llmResponseText,
              role: 'Assistant'
          });

      const completionResponse = await fpClient.recordings.create(
        {
  						projectId,
              allMessages: messages,
              inputs: input_variables,
              sessionInfo: session_info,
              promptVersionInfo: formattedPrompt.promptInfo,
              callInfo: getCallInfo(formattedPrompt.promptInfo, start, end),
              traceInfo: trace_info
          }
      )

      return {completionId: completionResponse.completionId, userMessages: formattedPrompt.llmPrompt, llmResponseText: llmResponseText};
  }

  const articles = [
      "george washington was the first president of the united states",
      "the sky is blue",
      "the earth is round",
      ""
  ];

  const questions = [
      "who was the first president of the united states?",
      "what color is the sky?",
      "what shape is the earth?",
      "repeat the first question and answer"
  ];


  const inputPairs = articles.map((article, index) => [article, questions[index]]);

  async function main() {
      const session = await fpClient.sessions.create();

      const history_messages = [];

      for (const [article, question] of inputPairs) {
          const traceInfo = await session.createTrace(question);
          const botResponse = await call(
              projectId, templateName, environment,
              { question: question, article: article }, history_messages, getSessionInfo(session), traceInfo
          );
          // update history
          history_messages.push(...botResponse.userMessages);
          history_messages.push({
              content: botResponse.llmResponseText,
              role: 'assistant'
          });
          console.log("Bot response: ", botResponse.llmResponseText);
          await traceInfo.recordOutput(projectId, botResponse.llmResponseText);
      }
  }

  main().catch(console.error);
  ```

  ```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 ai.freeplay.example.java.ThinExampleUtils.callAnthropic // this is a private helper function
  import com.fasterxml.jackson.databind.ObjectMapper
  import kotlinx.coroutines.future.await
  import kotlinx.coroutines.runBlocking

  private val objectMapper = ObjectMapper()

  fun main(): Unit = runBlocking {
      val freeplayApiKey = System.getenv("FREEPLAY_API_KEY")
      val projectId = System.getenv("FREEPLAY_PROJECT_ID")
      val baseUrl = System.getenv("FREEPLAY_API_URL") + "/api"
      val anthropicApiKey = System.getenv("ANTHROPIC_API_KEY")

      val fpClient = Freeplay(
          Freeplay.Config()
              .freeplayAPIKey(freeplayApiKey)
              .baseUrl(baseUrl)
      )

      val questions = listOf(
              "who was the first president of the united states?",
              "what color is the sky?",
              "what shape is the earth?",
              "repeat the first question and answer"
      )
      val articles = listOf(
              "george washington was the first president of the united states",
              "the sky is blue",
              "the earth is round",
              ""
      )
      val history = mutableListOf<ChatMessage>()

      println("Getting the prompt...")
      val template = fpClient.prompts()
          .get(
              projectId,
              "History-QA",
              "latest"
          ).await()

      val sessionInfo = fpClient.sessions().create()
              .customMetadata(mapOf("custom_field" to "custom_value"))
              .sessionInfo

      for (i in 1..questions.size){
          val variables = mapOf("question" to questions[i-1], "article" to articles[i-1])
          println("variables: $variables")

          val formatted = template.bind(variables, history).format<List<ChatMessage>>()

          println("Calling Anthropic...")
          val startTime = System.currentTimeMillis()
          val llmResponse = callAnthropic(
                  objectMapper,
                  anthropicApiKey,
                  formatted.promptInfo.model,
                  formatted.promptInfo.modelParameters,
                  formatted.formattedPrompt,
                  formatted.systemContent.orElse(null)
          ).await()

          val bodyNode = objectMapper.readTree(llmResponse.body())
          println("Completion: " + bodyNode.path("content").get(0).path("text").asText())

          println("Recording the result")
          val allMessages: List<ChatMessage> = formatted.allMessages(
                  ChatMessage("assistant", bodyNode.path("content").get(0).path("text").asText())
          )

          if (allMessages.size >= 2) {
              history.add(allMessages[allMessages.size - 2])
              history.add(allMessages[allMessages.size - 1])
          } else if (allMessages.isNotEmpty()) {
              history.addAll(allMessages)
          }

          val callInfo = CallInfo.from(
                  formatted.promptInfo,
                  startTime,
                  System.currentTimeMillis()
          )

          val recordResponse = fpClient.recordings().create(
          RecordInfo(
              projectId,
              allMessages
          ).inputs(variables)
              .sessionInfo(session.sessionInfo)
              .promptVersionInfo(prompt.promptInfo)
              .callInfo(callInfo)
              .traceInfo(trace)
          ).await()

      }
  }
  ```
</CodeGroup>
