Provider Message Formats¶
strahl.analyze() accepts transcripts in OpenAI and Anthropic message formats. You do not need to convert between them — pass the messages as you received them from your provider client.
OpenAI¶
Pass the messages list directly from your OpenAI conversation history:
from openai import OpenAI
import strahl
client = OpenAI()
messages = [{"role": "user", "content": "Find my order."}]
response = client.chat.completions.create(model="gpt-4o", messages=messages, tools=[...])
messages.append(response.choices[0].message.to_dict())
analysis = strahl.analyze(messages)
analysis.raise_if_denied()
OpenAI tool calls appear in the assistant message under tool_calls, with arguments as a JSON string:
{
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"function": {
"name": "lookup_order",
"arguments": '{"order_id": "ord_123"}',
},
}],
}
Tool results are role "tool" messages referencing the call by tool_call_id:
Anthropic¶
Pass the messages list from your Anthropic conversation:
from anthropic import Anthropic
import strahl
client = Anthropic()
messages = [{"role": "user", "content": "Find my order."}]
response = client.messages.create(model="claude-opus-4-7", messages=messages, tools=[...])
messages.append({"role": "assistant", "content": response.content})
analysis = strahl.analyze(messages)
analysis.raise_if_denied()
Anthropic tool calls appear as tool_use blocks in list-style message content:
{
"role": "assistant",
"content": [
{"type": "text", "text": "I will look that up."},
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "lookup_order",
"input": {"order_id": "ord_123"},
},
],
}
Tool results are tool_result blocks in the following user message:
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_abc123", "content": "Order found: ..."},
],
}
Requirements¶
- The transcript must end with an assistant message. Call
analyze()after the model responds, before executing tools. - Every non-tool-result message role must have a corresponding role label set via
set_role_labels(). Tool result messages (role"tool"in OpenAI, or puretool_resultlist content in Anthropic) are exempt. - Every tool referenced in the transcript must be registered with
add_tool()or@tool().
Registering provider tool schemas¶
You can register tools using your provider's schema format directly, without rewriting them:
import strahl
from strahl import Label
openai_tool = {
"type": "function",
"function": {
"name": "lookup_order",
"description": "Look up an order by ID.",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
},
}
anthropic_tool = {
"name": "lookup_order",
"description": "Look up an order by ID.",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
}
requires = Label(source={"user"}, visibility={"user"})
produces = Label(source={"orders"}, visibility={"user"})
strahl.add_tool(fn=openai_tool, requires=requires, produces=produces)
strahl.add_tool(fn=anthropic_tool, requires=requires, produces=produces)
The SDK detects the format from the schema structure.