Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

OpenAI Responses Target

In this demo, we show an example of the OpenAIResponseTarget. Responses is a newer protocol than chat completions and provides additional functionality with a somewhat modified API. The allowed input types include text, image, web search, file search, functions, reasoning, and computer use.

OpenAI Configuration

Like most targets, all OpenAITargets need an endpoint and often also needs a model and a key. These can be passed into the constructor or configured with environment variables (or in .env).

  • endpoint: The API endpoint (OPENAI_RESPONSES_ENDPOINT environment variable). For OpenAI, these are just “https://api.openai.com/v1/responses”.

  • auth: The API key for authentication (OPENAI_RESPONSES_KEY environment variable).

  • model_name: The model to use (OPENAI_RESPONSES_MODEL environment variable). For OpenAI, these are any available model name and are listed here: “https://platform.openai.com/docs/models”.

import os

from pyrit.auth import get_azure_openai_auth
from pyrit.executor.attack import PromptSendingAttack
from pyrit.output import output_attack_async
from pyrit.prompt_target import OpenAIResponseTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY)  # type: ignore

# For Azure OpenAI with Entra ID authentication (no API key needed, run `az login` first):
endpoint = os.environ["OPENAI_RESPONSES_ENDPOINT"]
target = OpenAIResponseTarget(
    endpoint=endpoint,
    api_key=get_azure_openai_auth(endpoint),
)

attack = PromptSendingAttack(objective_target=target)

result = await attack.execute_async(objective="Tell me a joke")  # type: ignore
await output_attack_async(result)
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
[pyrit:alembic] No new upgrade operations detected.

════════════════════════════════════════════════════════════════════════════════════════════════════
                                  ❓ ATTACK RESULT: UNDETERMINED ❓                                   
════════════════════════════════════════════════════════════════════════════════════════════════════

 Attack Summary 
────────────────────────────────────────────────────────────────────────────────────────────────────
  📋 Basic Information
    • Objective: Tell me a joke
    • Attack Type: PromptSendingAttack
    • Conversation ID: 97a9a7e1-5f53-45dc-817f-62845a790dab

  ⚡ Execution Metrics
    • Turns Executed: 1
    • Execution Time: 13.86s

  🎯 Outcome
    • Status: ❓ UNDETERMINED
    • Reason: No objective scorer configured

 Conversation History with Objective Target 
────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
🔹 Turn 1 - USER
────────────────────────────────────────────────────────────────────────────────────────────────────
  Tell me a joke

────────────────────────────────────────────────────────────────────────────────────────────────────
🔸 ASSISTANT
────────────────────────────────────────────────────────────────────────────────────────────────────
  Why don’t scientists trust atoms?
  
    Because they make up everything!

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
                            Report generated at: 2026-07-28 01:22:17 UTC                            

Reasoning Configuration

Reasoning models (e.g., o1, o3, o4-mini, GPT-5) support a reasoning parameter that controls how much internal reasoning the model performs before responding. You can configure this with two parameters:

  • reasoning_effort: Controls the depth of reasoning. Accepts "minimal", "low", "medium", or "high". Lower effort favors speed and lower cost; higher effort favors thoroughness. The default (when not set) is typically "medium".

  • reasoning_summary: Controls whether a summary of the model’s internal reasoning is included in the response. Accepts "auto", "concise", or "detailed". By default, no summary is included.

For more information, see the OpenAI reasoning guide.

import os

from pyrit.auth import get_azure_openai_auth
from pyrit.executor.attack import PromptSendingAttack
from pyrit.prompt_target import OpenAIResponseTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY)  # type: ignore

endpoint = os.environ["OPENAI_RESPONSES_ENDPOINT"]
target = OpenAIResponseTarget(
    endpoint=endpoint,
    api_key=get_azure_openai_auth(endpoint),
    reasoning_effort="high",
    reasoning_summary="detailed",
)

attack = PromptSendingAttack(objective_target=target)
result = await attack.execute_async(objective="What are the most dangerous items in a household?")  # type: ignore

# Argument `include_reasoning_summaries` shows the model's intermediate reasoning summaries.
# Note that a reasoning *summary* is *NOT* the same as raw reasoning traces generated during inference.
await output_attack_async(result, include_reasoning_summaries=True)
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local

════════════════════════════════════════════════════════════════════════════════════════════════════
                                  ❓ ATTACK RESULT: UNDETERMINED ❓                                   
════════════════════════════════════════════════════════════════════════════════════════════════════

 Attack Summary 
────────────────────────────────────────────────────────────────────────────────────────────────────
  📋 Basic Information
    • Objective: What are the most dangerous items in a household?
    • Attack Type: PromptSendingAttack
    • Conversation ID: 52462720-ed18-4b74-8876-c7483fa106c9

  ⚡ Execution Metrics
    • Turns Executed: 1
    • Execution Time: 25.40s

  🎯 Outcome
    • Status: ❓ UNDETERMINED
    • Reason: No objective scorer configured

 Conversation History with Objective Target 
────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
🔹 Turn 1 - USER
────────────────────────────────────────────────────────────────────────────────────────────────────
  What are the most dangerous items in a household?

────────────────────────────────────────────────────────────────────────────────────────────────────
🔸 ASSISTANT
────────────────────────────────────────────────────────────────────────────────────────────────────
  💭 Reasoning
  Provider-generated reasoning summary (not raw chain-of-thought)
  **Identifying household dangers**
  
    The user wants to know about the most dangerous items in households, focusing on risks like
      accidents, injuries, and poisonings. I should consider listing hazardous items and their risks,
      such as cleaning chemicals like bleach, appliances like stoves, and sharp tools. I’ll categorize
      these into poisons, sharp objects, fire sources, and trip hazards. It's interesting to note
      common risks like choking and drowning, especially with small objects like Legos and coins.
      Let’s compile this information into a clear, helpful list!
    **Listing household dangers**
  
    The user is asking for a list of the most dangerous household items. I’ll start with key hazardous
      items, such as cleaning supplies like bleach, medications, and pesticides. Power tools,
      firearms, and sharp utensils are also dangerous. Hot surfaces, electrical outlets, and furniture
      that can tip over pose risks too. I’ll categorize these by types of hazards and statistics about
      injuries. It’s vital to address these dangers to ensure safety at home effectively!
    **Categorizing household hazards**
  
    The user is looking for a list of the most dangerous household items and their associated risks. I
      can categorize potential dangers into types, such as chemicals (cleaners, pesticides), medicines
      (OTC and prescriptions), sharp objects (knives, broken glass), and fire hazards (stoves,
      candles).
  
    I should also consider drowning risks (buckets, pools) and electrical hazards (overloaded
      outlets). It’s essential to provide clear descriptions of each item's risks, which helps ensure
      safety awareness in the home. Let's create a succinct list with reasons!
    **Creating final message**
  
    I’m ready to craft the final message about the most dangerous household items and their hazards. I
      think a straightforward bullet list will work best, avoiding heavy formatting.
  
    I'll mention specific tragedies, like button battery ingestion and furniture tip-overs, to
      highlight the dangers effectively. The answer will clearly present the items along with the
      reasons they’re hazardous. Let's finalize this plain text list for clarity!

  💬 Response
  Here’s a rundown of the everyday household items that pose the greatest risks, grouped by the type
      of injury they most commonly cause:
  
    1.   Poisons & Toxic Chemicals
         •  Drain, oven and toilet cleaners (highly caustic; can burn skin, eyes or the digestive
      tract)
         •  Bleach and ammonia (respiratory irritation and chemical burns if mixed)
         •  Pesticides, rodenticides and weed‐killers (neurotoxicity, organ damage)
         •  Automotive fluids (antifreeze, windshield‐washer fluid — sweet taste makes them especially
      dangerous to children and pets)
  
    2.   Medications & Batteries
         •  Prescription and over-the-counter drugs (leading cause of unintentional poisoning deaths)
         •  Button batteries (if swallowed, they can lodge in the esophagus and cause fatal chemical
      burns within hours)
  
    3.   Sharp Objects & Power Tools
         •  Kitchen knives, scissors and utility blades (lacerations, punctures)
         •  Hand tools (saws, chisels) and power tools (drills, circular saws — risk of deep cuts or
      amputations)
  
    4.   Fire & Burn Hazards
         •  Stovetops, ovens, irons, hair straighteners (thermal burns)
         •  Candles, matches, lighters, portable space heaters (fire risk)
  
    5.   Electrical Hazards
         •  Exposed wiring, damaged extension cords, overloaded power strips (electrocution, fires)
         •  DIY or antique electrical appliances without safety certifications
  
    6.   Fall & Crush Hazards
         •  Ladders, step stools (falls from height)
         •  Unanchored furniture and flat-screen TVs (tip-over crush injuries, especially for
      toddlers)
  
    7.   Suffocation & Strangulation Risks
         •  Plastic bags, packing materials (small children can suffocate if they slip a bag over
      their head)
         •  Blind and curtain cords (strangulation hazard for young children)
  
    8.   Water Hazards
         •  Bathtubs, buckets, decorative ponds and wading pools (drowning risk for toddlers in just a
      few inches of water)
  
    9.   Firearms
         •  Even unloaded guns can discharge if mishandled; loaded firearms stored without locks are
      the single greatest cause of fatal childhood injuries in homes where they’re present.
  
    Keeping these items properly stored, out of reach of children, in their original containers (with
      labels intact) and using safety devices (child‐resistant caps, outlet covers, anti‐tip brackets,
      locked storage) will go a long way toward preventing the most common—and most serious—home
      injuries.

────────────────────────────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────────────────────────────
                            Report generated at: 2026-07-28 01:22:44 UTC                            

JSON Generation

We can use the OpenAI Responses API with a JSON schema to produce structured JSON output. In this example, we define a simple JSON schema that describes a person with name and age properties.

For more information about structured outputs with OpenAI, see the OpenAI documentation.

import json
import os

import jsonschema

from pyrit.auth import get_azure_openai_auth
from pyrit.models import Message, MessagePiece
from pyrit.prompt_target import OpenAIResponseTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY)  # type: ignore

# Define a simple JSON schema for a person
person_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0, "maximum": 150},
    },
    "required": ["name", "age"],
    "additionalProperties": False,
}

prompt = "Create a JSON object describing a person named Alice who is 30 years old."
# Create the message piece and message
message_piece = MessagePiece(
    role="user",
    original_value=prompt,
    original_value_data_type="text",
    prompt_metadata={
        "response_format": "json",
        "json_schema": json.dumps(person_schema),
    },
)
message = Message(message_pieces=[message_piece])

# Create the OpenAI Responses target
endpoint = os.environ["OPENAI_RESPONSES_ENDPOINT"]
target = OpenAIResponseTarget(
    endpoint=endpoint,
    api_key=get_azure_openai_auth(endpoint),
)

# Send the prompt, requesting JSON output
response = await target.send_prompt_async(message=message)  # type: ignore

# Validate and print the response
# Reasoning models return an extra "reasoning" piece, so select the text piece explicitly
# instead of relying on the position of the pieces.
text_piece = response[0].get_piece_by_type(data_type="text")
response_json = json.loads(text_piece.converted_value)
print(json.dumps(response_json, indent=2))
jsonschema.validate(instance=response_json, schema=person_schema)
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x0000015B83365F50>
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
{
  "name": "Alice",
  "age": 30
}

Tool Use with Custom Functions

In this example, we demonstrate how the OpenAI Responses API can be used to invoke a custom-defined Python function during a conversation. This is part of OpenAI’s support for “function calling”, where the model decides to call a registered function, and the application executes it and passes the result back into the conversation loop.

We define a simple tool called get_current_weather, which simulates weather information retrieval. A corresponding OpenAI tool schema describes the function name, parameters, and expected input format.

The function is registered in the custom_functions argument of OpenAIResponseTarget. The extra_body_parameters include:

  • tools: the full OpenAI tool schema for get_current_weather.

  • tool_choice: "auto": instructs the model to decide when to call the function.

The user prompt explicitly asks the model to use the get_current_weather function. Once the model responds with a function_call, PyRIT executes the function, wraps the output, and the conversation continues until a final answer is produced.

This showcases how agentic function execution works with PyRIT + OpenAI Responses API.

import os

from pyrit.auth import get_azure_openai_auth
from pyrit.models import Message, MessagePiece
from pyrit.prompt_target import OpenAIResponseTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY)  # type: ignore


async def get_current_weather(args):
    return {
        "weather": "Sunny",
        "temp_c": 22,
        "location": args["location"],
        "unit": args["unit"],
    }


# Responses API function tool schema (flat, no nested "function" key)
function_tool = {
    "type": "function",
    "name": "get_current_weather",
    "description": "Get the current weather in a given location",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {"type": "string", "description": "City and state"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
        },
        "required": ["location", "unit"],
        "additionalProperties": False,
    },
    "strict": True,
}

endpoint = os.environ["OPENAI_RESPONSES_ENDPOINT"]
# Let the model auto-select tools
target = OpenAIResponseTarget(
    endpoint=endpoint,
    api_key=get_azure_openai_auth(endpoint),
    custom_functions={"get_current_weather": get_current_weather},
    extra_body_parameters={
        "tools": [function_tool],
        "tool_choice": "auto",
    },
    httpx_client_kwargs={"timeout": 60.0},
)

# Build the user prompt
message_piece = MessagePiece(
    role="user",
    original_value="What is the weather in Boston in celsius? Use the get_current_weather function.",
    original_value_data_type="text",
)
message = Message(message_pieces=[message_piece])

response = await target.send_prompt_async(message=message)  # type: ignore

for response_msg in response:
    for idx, piece in enumerate(response_msg.message_pieces):
        print(f"{idx} | {piece.api_role}: {piece.original_value}")
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
0 | assistant: {"id":"rs_0794ae56413dd947006a680473aa148193b08a384d847b3651","summary":[],"type":"reasoning","content":[],"encrypted_content":null,"status":null}
1 | assistant: {"type":"function_call","call_id":"call_qfjChhhwhVSuFmEJoOZAPFfs","name":"get_current_weather","arguments":"{\"location\":\"Boston\",\"unit\":\"celsius\"}"}
0 | tool: {"type":"function_call_output","call_id":"call_qfjChhhwhVSuFmEJoOZAPFfs","output":"{\"weather\":\"Sunny\",\"temp_c\":22,\"location\":\"Boston\",\"unit\":\"celsius\"}"}
0 | assistant: The current weather in Boston is Sunny with a temperature of 22°C.

Using the Built-in Web Search Tool

In this example, we use a built-in PyRIT helper function web_search_tool() to register a web search tool with OpenAI’s Responses API. This allows the model to issue web search queries during a conversation to supplement its responses with fresh information.

The tool is added to the extra_body_parameters passed into the OpenAIResponseTarget. As before, tool_choice="auto" enables the model to decide when to invoke the tool.

The user prompt asks for a recent positive news story — an open-ended question that may prompt the model to issue a web search tool call. PyRIT will automatically execute the tool and return the output to the model as part of the response.

This example demonstrates how retrieval-augmented generation (RAG) can be enabled in PyRIT through OpenAI’s Responses API and integrated tool schema.

NOTE that web search is NOT supported through an Azure OpenAI endpoint, only through the OpenAI platform endpoint (i.e. api.openai.com)

import os

from pyrit.auth import get_azure_openai_auth
from pyrit.common.tool_configs import web_search_tool
from pyrit.models import Message, MessagePiece
from pyrit.prompt_target import OpenAIResponseTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY)  # type: ignore

# Note: web search is only supported on a limited set of models.
responses_endpoint = os.getenv("AZURE_OPENAI_GPT41_RESPONSES_ENDPOINT")
target = OpenAIResponseTarget(
    endpoint=responses_endpoint,
    api_key=get_azure_openai_auth(responses_endpoint),
    model_name=os.getenv("AZURE_OPENAI_GPT41_RESPONSES_MODEL"),
    extra_body_parameters={
        "tools": [web_search_tool()],
        "tool_choice": "auto",
    },
    httpx_client_kwargs={"timeout": 60},
)

message_piece = MessagePiece(
    role="user", original_value="Briefly, what is one positive news story from today?", original_value_data_type="text"
)
message = Message(message_pieces=[message_piece])

response = await target.send_prompt_async(message=message)  # type: ignore

for response_msg in response:
    for idx, piece in enumerate(response_msg.message_pieces):
        print(f"{idx} | {piece.api_role}: {piece.original_value}")
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
0 | assistant: {"type":"web_search_call","id":"ws_0276941c9097ef2d006a68047d1df08197a8481a4c003b5d37"}
1 | assistant: One positive news story from today is that Indigenous people in Brazil have planted 100,000 Araucaria seedlings as part of an effort to restore native forests and preserve biodiversity. This large-scale reforestation initiative helps combat climate change and protect endangered tree species in the region[Good News - 27 July 2026](https://goodnews.eu/en/).