Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/src/components/Config/CreateTargetDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default function CreateTargetDialog({ open, onClose, onCreated, existingT
const [temperature, setTemperature] = useState('1.0')
const [topP, setTopP] = useState('1.0')
const [repetitionPenalty, setRepetitionPenalty] = useState('1.0')
const [imageUrlAsString, setImageUrlAsString] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [error, setError] = useState<string | null>(null)
const [fieldErrors, setFieldErrors] = useState<{ targetType?: string; endpoint?: string }>({})
Expand All @@ -165,6 +166,7 @@ export default function CreateTargetDialog({ open, onClose, onCreated, existingT
const isRoundRobin = targetConfig?.kind === 'roundrobin'
const isAzureML = targetConfig?.kind === 'azureml'
const isOpenAi = targetConfig?.kind === 'openai'
const isOpenAiResponse = targetType === 'OpenAIResponseTarget'
const supportsEntra = targetConfig?.supportsEntra ?? false
const showAuthField = targetType !== '' && supportsEntra
const isEntra = showAuthField && authMode === 'entra'
Expand Down Expand Up @@ -252,6 +254,7 @@ export default function CreateTargetDialog({ open, onClose, onCreated, existingT
setTemperature('1.0')
setTopP('1.0')
setRepetitionPenalty('1.0')
setImageUrlAsString(false)
setError(null)
setFieldErrors({})
setSelectedInnerTargets([])
Expand Down Expand Up @@ -337,6 +340,10 @@ export default function CreateTargetDialog({ open, onClose, onCreated, existingT
if (!isNaN(parsedRepetitionPenalty)) params.repetition_penalty = parsedRepetitionPenalty
}

if (isOpenAiResponse && imageUrlAsString) {
params.image_url_as_string = true
}

await targetsApi.createTarget({
type: targetType,
params,
Expand Down Expand Up @@ -577,6 +584,20 @@ export default function CreateTargetDialog({ open, onClose, onCreated, existingT
</>
)}

{isOpenAiResponse && (
<div>
<Switch
checked={imageUrlAsString}
onChange={(_, data) => setImageUrlAsString(data.checked)}
label="Send image_url as bare string"
/>
<Text size={200} style={{ color: tokens.colorNeutralForeground3, display: 'block', marginTop: '2px' }}>
Some Responses-API endpoints (e.g. Corvid) require <code>image_url</code> to be a bare data-URL
string rather than an object. Leave off for standard OpenAI / Azure OpenAI.
</Text>
</div>
)}

{showAuthField && (
<Field label="Authentication">
<RadioGroup
Expand Down
8 changes: 7 additions & 1 deletion pyrit/prompt_target/openai/openai_response_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def __init__(
reasoning_summary: Literal["auto", "concise", "detailed"] | None = None,
extra_body_parameters: dict[str, Any] | None = None,
fail_on_missing_function: bool = False,
image_url_as_string: bool = False,
custom_configuration: TargetConfiguration | None = None,
**kwargs: Any,
) -> None:
Expand Down Expand Up @@ -169,6 +170,10 @@ def __init__(
self._custom_functions: dict[str, ToolExecutor] = custom_functions or {}
self._fail_on_missing_function: bool = fail_on_missing_function

# Some Responses API endpoints (e.g. Corvid) require image_url as a bare string
# rather than the {"url": ...} object used by Chat Completions.
self._image_url_as_string: bool = image_url_as_string

# Extract the grammar 'tool' if one is present
# See
# https://platform.openai.com/docs/guides/function-calling#context-free-grammars
Expand Down Expand Up @@ -238,7 +243,8 @@ async def _construct_input_item_from_piece_async(self, piece: MessagePiece) -> d
}
if piece.converted_value_data_type == "image_path":
data_url = await convert_local_image_to_data_url_async(piece.converted_value)
return {"type": "input_image", "image_url": {"url": data_url}}
image_url: Any = data_url if self._image_url_as_string else {"url": data_url}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should do an analysis of the API docs by OpenAI, then try both with all the OpenAI response targets we have and see what works. I would prefer not to support both if possible.

return {"type": "input_image", "image_url": image_url}
raise ValueError(f"Unsupported piece type for inline content: {piece.converted_value_data_type}")

async def _build_input_for_multi_modal_async(self, conversation: MutableSequence[Message]) -> list[dict[str, Any]]:
Expand Down
5 changes: 4 additions & 1 deletion pyrit/setup/initializers/components/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ class TargetConfig:
key_var="AZURE_OPENAI_GPT5_KEY",
model_var="AZURE_OPENAI_GPT5_MODEL",
underlying_model_var="AZURE_OPENAI_GPT5_UNDERLYING_MODEL",
extra_kwargs={"image_url_as_string": True},
),
TargetConfig(
registry_name="azure_openai_gpt5_responses_high_reasoning",
Expand All @@ -297,14 +298,15 @@ class TargetConfig:
key_var="AZURE_OPENAI_GPT5_KEY",
model_var="AZURE_OPENAI_GPT5_MODEL",
underlying_model_var="AZURE_OPENAI_GPT5_UNDERLYING_MODEL",
extra_kwargs={"extra_body_parameters": {"reasoning": {"effort": "high"}}},
extra_kwargs={"extra_body_parameters": {"reasoning": {"effort": "high"}}, "image_url_as_string": True},
),
TargetConfig(
registry_name="platform_openai_responses",
target_class=OpenAIResponseTarget,
endpoint_var="PLATFORM_OPENAI_RESPONSES_ENDPOINT",
key_var="PLATFORM_OPENAI_RESPONSES_KEY",
model_var="PLATFORM_OPENAI_RESPONSES_MODEL",
extra_kwargs={"image_url_as_string": True},
),
TargetConfig(
registry_name="azure_openai_responses",
Expand All @@ -313,6 +315,7 @@ class TargetConfig:
key_var="AZURE_OPENAI_RESPONSES_KEY",
model_var="AZURE_OPENAI_RESPONSES_MODEL",
underlying_model_var="AZURE_OPENAI_RESPONSES_UNDERLYING_MODEL",
extra_kwargs={"image_url_as_string": True},
),
# ============================================
# Realtime Targets (RealtimeTarget)
Expand Down
Loading