1. Structred outputs
This shows how to use structured outputs with Freeplay’s SDKExamples
import os
import time
from typing import List
import pydantic
from openai import OpenAI
from freeplay import Freeplay, RecordPayload, CallInfo
from freeplay.resources.recordings import UsageTokens
# Define structured output classes with Pydantic
class COTStep(pydantic.BaseModel):
thinking: str
result: str
class COTResponse(pydantic.BaseModel):
response: str
steps: List[COTStep]
# Initialize clients
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"))
input_variables = {"question": "why is the sky blue?"}
project_id = os.environ["FREEPLAY_PROJECT_ID"]
# Fetch formatted prompt with output schema
formatted_prompt = fp_client.prompts.get_formatted(
project_id=project_id,
template_name="my-chat-template",
environment="latest",
variables=input_variables,
)
print(f"Tool schema: {formatted_prompt.tool_schema}")
print(f"Output schema: {formatted_prompt.formatted_output_schema}")
start = time.time()
# Build the completion parameters
completion_params = {
**formatted_prompt.prompt_info.model_parameters,
}
# Add tools if present
if formatted_prompt.tool_schema:
completion_params["tools"] = formatted_prompt.tool_schema
# Use the output schema from the prompt template
completion = client.chat.completions.create(
messages=formatted_prompt.llm_prompt,
model=formatted_prompt.prompt_info.model,
response_format={
"type": "json_schema",
"json_schema": {
"strict": True,
"schema": COTResponse.model_json_schema(), # OR you can use formatted_prompt.formatted_output_schema,
"name": "COTReasoning",
},
}
if formatted_prompt.formatted_output_schema
else openai.NotGiven(),
)
end = time.time()
print("Completion with prompt schema: %s" % completion)
# Record to Freeplay
session = fp_client.sessions.create()
messages = formatted_prompt.all_messages(completion.choices[0].message)
call_info = CallInfo.from_prompt_info(
formatted_prompt.prompt_info,
start,
end,
UsageTokens(completion.usage.prompt_tokens, completion.usage.completion_tokens),
api_style="batch",
)
record_response = 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=call_info,
tool_schema=formatted_prompt.tool_schema,
output_schema=COTResponse.model_json_schema() # OR you can use formatted_prompt.formatted_output_schema
)
)
import OpenAI from "openai";
import { z } from "zod";
import Freeplay, { getSessionInfo } from "@freeplay/freeplay";
// Define structured output schemas using Zod
const COTStepSchema = z.object({
thinking: z.string(),
result: z.string(),
});
const COTResponseSchema = z.object({
response: z.string(),
steps: z.array(COTStepSchema),
});
async function main() {
const fpClient = new Freeplay({
freeplayApiKey: process.env["FREEPLAY_API_KEY"],
baseUrl: `${process.env["FREEPLAY_API_URL"]}/api`,
});
const openaiClient = new OpenAI({
apiKey: process.env["OPENAI_API_KEY"],
});
const inputVariables = { question: "why is the sky blue?" };
const projectId = process.env["FREEPLAY_PROJECT_ID"];
// Fetch formatted prompt with output schema
const formattedPrompt = await fpClient.prompts.getFormatted({
projectId,
templateName: "my-chat-template",
environment: "latest",
variables: inputVariables,
});
console.log("Tool schema:", formattedPrompt.toolSchema);
console.log("Output schema:", formattedPrompt.outputSchema);
const start = new Date();
// Build the completion parameters
const completionParams: OpenAI.ChatCompletionCreateParams = {
messages: (formattedPrompt.llmPrompt || []) as OpenAI.ChatCompletionMessageParam[],
model: formattedPrompt.promptInfo.model,
...formattedPrompt.promptInfo.modelParameters,
};
// Add tools if present
if (formattedPrompt.toolSchema) {
completionParams.tools = formattedPrompt.toolSchema;
}
// Use output schema from prompt template, or fall back to Zod schema
let completion: OpenAI.ChatCompletion;
let jsonSchema: any = undefined;
if (formattedPrompt.outputSchema) {
// Use schema from prompt template
completion = await openaiClient.chat.completions.create({
...completionParams,
response_format: {
type: "json_schema",
json_schema: {
strict: true,
schema: formattedPrompt.outputSchema,
name: "COTReasoning",
},
},
});
console.log("Completion with prompt schema:", completion);
} else {
// Alternatively, use a Zod schema directly with Zod 4's native toJSONSchema()
jsonSchema = z.toJSONSchema(COTResponseSchema);
completion = await openaiClient.chat.completions.create({
...completionParams,
response_format: {
type: "json_schema",
json_schema: {
strict: true,
schema: jsonSchema,
name: "COTReasoning",
},
},
});
console.log("Completion with Zod schema:", completion);
}
const end = new Date();
// Record to Freeplay
const session = fpClient.sessions.create();
const messages = formattedPrompt.allMessages(completion.choices[0].message);
await fpClient.recordings.create({
projectId,
allMessages: messages,
sessionInfo: getSessionInfo(session),
inputs: inputVariables,
promptVersionInfo: formattedPrompt.promptInfo,
callInfo: {
provider: formattedPrompt.promptInfo.provider,
model: formattedPrompt.promptInfo.model,
startTime: start,
endTime: end,
modelParameters: formattedPrompt.promptInfo.modelParameters,
usage: completion.usage
? {
promptTokens: completion.usage.prompt_tokens,
completionTokens: completion.usage.completion_tokens,
}
: undefined,
},
toolSchema: formattedPrompt.toolSchema,
outputSchema: formattedPrompt.outputSchema || jsonSchema
});
console.log("Recording created successfully");
}
main().catch(console.error);

