1. Create client
2. Define a Call and Record Helper
3. Pass through Trace Info to Record
Pass trace info through on the record call to freeplay4. Loop over questions and record to traces
5. Create a Trace Object
Create a Trace Object, including a user display input message6. Close and Record the Trace
Record the Trace with the final display output to close the traceExamples
import os
import random
import time
from typing import Optional
from anthropic import Anthropic, NotGiven
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']
client = Anthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY")
)
def call_and_record(
project_id: str,
template_name: str,
env: str,
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
)
print(f"Ready for LLM: {formatted_prompt.llm_prompt}")
start = time.time()
completion = 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)
all_messages = formatted_prompt.all_messages(
new_message={'role': 'assistant', 'content': llm_response}
)
call_info = CallInfo.from_prompt_info(formatted_prompt.prompt_info, start, end)
record_response = fp_client.recordings.create(
RecordPayload(
project_id=project_id,
all_messages=all_messages,
session_info=session_info,
inputs=input_variables,
prompt_version_info=formatted_prompt.prompt_info,
call_info=call_info,
trace_info=trace_info
)
)
return {'completion_id': record_response.completion_id, 'llm_response': llm_response}
# send 3 questions to the model encapsulated into a trace
user_questions = ["answer life's most existential questions", "what is sand?", "how tall are lions?"]
session = fp_client.sessions.create()
for question in user_questions:
trace_info = session.create_trace(input=question)
bot_response = call_and_record(
project_id=project_id,
template_name='my-anthropic-prompt',
env='latest',
input_variables={'question': question},
session_info=session.session_info,
trace_info=trace_info
)
categorization_result = call_and_record(
project_id=project_id,
template_name='question-classifier',
env='latest',
input_variables={'question': question},
session_info=session.session_info,
trace_info=trace_info
)
trace_info.record_output(project_id, bot_response['llm_response'])
print(f"Trace info id: {trace_info.trace_id}")
import Freeplay, { getCallInfo, getSessionInfo } from "freeplay/thin";
import Anthropic from "@anthropic-ai/sdk";
const projectId = process.env["FREEPLAY_PROJECT_ID"];
const environment = "latest";
const templateName = "my-prompt-anthropic";
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,
session_info,
trace_info
) {
let formattedPrompt = await fpClient.prompts.getFormatted({
projectId,
templateName,
environment,
variables: input_variables,
});
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,
llmResponseText: llmResponseText,
};
}
const userQuestion = "answer life's most existential questions";
const session = await fpClient.sessions.create({
customMetadata: { some_custom_metadata: 42 },
});
const traceInfo = await session.createTrace(userQuestion);
const botResponse = await call(
projectId,
templateName,
environment,
{ question: userQuestion },
getSessionInfo(session),
traceInfo
);
const categorizationResponse = await call(
projectId,
templateName,
environment,
{ question: `categorize this question ${userQuestion}` },
getSessionInfo(session),
traceInfo
);
await traceInfo.recordOutput(projectId, botResponse.llmResponseText);
console.log(
`Trace recorded with Id ${traceInfo.traceId} and input "${traceInfo.input}" and output "${botResponse.llmResponseText}"`
);
package ai.freeplay.example.java;
import ai.freeplay.client.thin.Freeplay;
import ai.freeplay.client.thin.resources.prompts.ChatMessage;
import ai.freeplay.client.thin.resources.prompts.FormattedPrompt;
import ai.freeplay.client.thin.resources.recordings.CallInfo;
import ai.freeplay.client.thin.resources.recordings.RecordInfo;
import ai.freeplay.client.thin.resources.sessions.Session;
import ai.freeplay.client.thin.resources.sessions.TraceInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static ai.freeplay.client.thin.Freeplay.Config;
import static ai.freeplay.example.java.ThinExampleUtils.callAnthropic;
import static java.lang.String.format;
public class ThinTrace {
static String freeplayApiKey = System.getenv("FREEPLAY_API_KEY");
static String projectId = System.getenv("FREEPLAY_PROJECT_ID");
static String customerDomain = System.getenv("FREEPLAY_CUSTOMER_NAME");
static String anthropicApiKey = System.getenv("ANTHROPIC_API_KEY");
private static final ObjectMapper objectMapper = new ObjectMapper();
static Freeplay fpClient = new Freeplay(Config()
.freeplayAPIKey(freeplayApiKey)
.customerDomain(customerDomain)
);
public static String call(
String projectId,
String templateName,
String environment,
Map<String, Object> variables,
Session session,
TraceInfo traceInfo
) {
return fpClient.prompts()
.<List<ChatMessage>>getFormatted(
projectId,
templateName,
environment,
variables,
null
).thenCompose((FormattedPrompt<List<ChatMessage>> formattedPrompt) -> {
long startTime = System.currentTimeMillis();
return callAnthropic(
objectMapper,
anthropicApiKey,
formattedPrompt.getPromptInfo().getModel(),
formattedPrompt.getPromptInfo().getModelParameters(),
formattedPrompt.getFormattedPrompt(),
formattedPrompt.getSystemContent().orElse(null)
).thenApply((HttpResponse<String> response) ->
new ThinExampleUtils.Tuple3<>(formattedPrompt, response, startTime)
);
}
).thenCompose((ThinExampleUtils.Tuple3<FormattedPrompt<List<ChatMessage>>, HttpResponse<String>, Long> promptAndResponse) -> {
FormattedPrompt<List<ChatMessage>> formattedPrompt = promptAndResponse.first;
HttpResponse<String> response = promptAndResponse.second;
long startTime = promptAndResponse.third;
JsonNode bodyNode;
try {
bodyNode = objectMapper.readTree(response.body());
} catch (JsonProcessingException e) {
throw new RuntimeException("Unable to parse response body.", e);
}
List<ChatMessage> allMessages = formattedPrompt.allMessages(
new ChatMessage("assistant", bodyNode.path("content").get(0).path("text").asText())
);
CallInfo callInfo = CallInfo.from(
formattedPrompt.getPromptInfo(),
startTime,
System.currentTimeMillis()
);
String output = bodyNode.path("content").get(0).path("text").asText();
System.out.println("Completion: " + output);
RecordInfo recordInfo = new RecordInfo(
projectId,
allMessages
).inputs(variables)
.sessionInfo(sessionInfo)
.promptVersionInfo(formattedPrompt.getPromptInfo())
.callInfo(callInfo)
.traceInfo(traceInfo));
recordInfo.traceInfo(traceInfo);
fpClient.recordings().create(recordInfo);
return CompletableFuture.completedFuture(output);
}
)
.exceptionally(exception -> {
System.out.println("Got exception: " + exception.getMessage());
return null;
})
.join();
}
public static void main(String[] args) throws ExecutionException, InterruptedException{
String input = "What is the meaning of life?";
Map<String, Object> inputVars = Map.of("question", input);
Session session = fpClient.sessions().create();
TraceInfo traceInfo = session.createTrace(input);
String response = call(
projectId,
"my-anthropic-prompt",
"latest",
inputVars,
session,
traceInfo
);
System.out.println("First Completion: " + response);
Map<String, Object> inputVars2 = Map.of("question", format("categorize the following question: %s", input));
String category = call(
projectId,
"my-anthropic-prompt",
"latest",
inputVars2,
session,
traceInfo
);
traceInfo.recordOutput(projectId, response);
System.out.println("Second Completion: " + category);
System.out.println("Recorded Trace " + traceInfo.traceId + " to session " + traceInfo.sessionId + " with input " + traceInfo.input + " and output " + response);
}
}

