Source code for pyrit.prompt_target.playwright_target
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from typing import TYPE_CHECKING, Optional, Protocol
from pyrit.models import (
Message,
MessagePiece,
construct_response_from_request,
)
from pyrit.prompt_target import PromptTarget, limit_requests_per_minute
# Avoid errors for users who don't have playwright installed
if TYPE_CHECKING:
from playwright.async_api import Page
else:
Page = None
class InteractionFunction(Protocol):
"""
Defines the structure of interaction functions used with PlaywrightTarget.
"""
async def __call__(self, page: "Page", message_piece: MessagePiece) -> str: ...
[docs]
class PlaywrightTarget(PromptTarget):
"""
PlaywrightTarget uses Playwright to interact with a web UI.
Parameters:
interaction_func (InteractionFunction): The function that defines how to interact with the page.
page (Page): The Playwright page object to use for interaction.
"""
[docs]
def __init__(
self,
*,
interaction_func: InteractionFunction,
page: "Page",
max_requests_per_minute: Optional[int] = None,
) -> None:
"""
Initialize the Playwright target.
Args:
interaction_func (InteractionFunction): The function that defines how to interact with the page.
page (Page): The Playwright page object to use for interaction.
max_requests_per_minute (int, Optional): Number of requests the target can handle per
minute before hitting a rate limit. The number of requests sent to the target
will be capped at the value provided.
"""
endpoint = page.url if page else ""
super().__init__(max_requests_per_minute=max_requests_per_minute, endpoint=endpoint)
self._interaction_func = interaction_func
self._page = page
@limit_requests_per_minute
async def send_prompt_async(self, *, prompt_request: Message) -> Message:
self._validate_request(prompt_request=prompt_request)
if not self._page:
raise RuntimeError(
"Playwright page is not initialized. Please pass a Page object when initializing PlaywrightTarget."
)
message_piece = prompt_request.message_pieces[0]
try:
text = await self._interaction_func(self._page, message_piece)
except Exception as e:
raise RuntimeError(f"An error occurred during interaction: {str(e)}") from e
response_entry = construct_response_from_request(request=message_piece, response_text_pieces=[text])
return response_entry
def _validate_request(self, *, prompt_request: Message) -> None:
n_pieces = len(prompt_request.message_pieces)
if n_pieces != 1:
raise ValueError(f"This target only supports a single message piece. Received: {n_pieces} pieces.")
piece_type = prompt_request.message_pieces[0].converted_value_data_type
if piece_type != "text":
raise ValueError(f"This target only supports text prompt input. Received: {piece_type}.")