Skip to content

OpenAI Message Format

strahl.analyze() currently accepts OpenAI-style message dictionaries.

Call it after the assistant response that requests tool calls, before executing those tools.

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",
    "content": None,
    "tool_calls": [
        {
            "id": "call_abc123",
            "function": {
                "name": "lookup_order",
                "arguments": '{"order_id": "ord_123"}',
            },
        }
    ],
}

Tool results from earlier turns are role "tool" messages referencing the call by tool_call_id:

{"role": "tool", "tool_call_id": "call_abc123", "content": "Order found: ..."}

Requirements

  • The final trace item must be a pending assistant tool call.
  • Every non-tool message role must have a role label.
  • Every final tool call must reference a registered tool.
  • Tool call arguments must be valid JSON objects.

Registering OpenAI Tool Schemas

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"],
        },
    },
}

strahl.add_tool(
    fn=openai_tool,
    requires=Label(source={"user"}, visibility={"user"}),
    produces=Label(source={"orders"}, visibility={"user"}),
)