diff --git a/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md b/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md index 4ddf831..c4d1465 100644 --- a/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md +++ b/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md @@ -458,6 +458,9 @@ planning; the tool loads and probes the media at runtime inside the workflow. | custom_auth_env_var | No | Env var holding the endpoint credential (default `TARGET_API_KEY`). | | custom_request_template | No | JSON template with `{prompt}` / `{image_b64}` / `{audio_b64}` / `{video_b64}` placeholders. | | custom_response_text_path | No | JSONPath to the response text (e.g. `$.response`). | +| custom_transport | No | `http` (default) or `streaming` for a realtime speech-to-speech target. | +| custom_protocol | No | Streaming protocol when `custom_transport=streaming`. Supported: `nova_sonic` (Amazon Nova Sonic S2S). | +| custom_region / custom_voice / custom_system_prompt / custom_model_id | No | Nova Sonic options (defaults: `us-east-1`, `matthew`, a generic assistant prompt, `amazon.nova-sonic-v1:0`). | | score_media_output | No | Score the target's GENERATED media (image-out / speech-to-speech), not just text. | | media_output_modalities | No | Output modalities to score (`image`/`audio`/`video`); defaults to all when `score_media_output`. | | media_output_rubric | No | Rubric for scoring generated media (defaults to the jailbreak rubric). | @@ -465,6 +468,15 @@ planning; the tool loads and probes the media at runtime inside the workflow. One attack runs per media file; folders fan out to one attack per file. Findings render each message part (input image/audio + the model's response) in the platform's finding detail. +**Speech-to-speech target (Nova Sonic).** When the user wants to red-team a realtime voice / +speech-to-speech model, use `custom_transport="streaming"` + `custom_protocol="nova_sonic"` with +`audio_paths`/`audio_dir` (the spoken attack). The SDK drives the Amazon Nova Sonic bidirectional +stream and returns the model's spoken reply (audio + transcript), which is scored. Requires AWS +credentials in the environment (env vars / role / platform secrets) and Bedrock access to Nova +Sonic in `custom_region` — no API key. Optional: `custom_voice`, `custom_system_prompt`, +`custom_model_id`. Note: Nova Sonic is well-aligned and typically returns a spoken refusal (a +`refusal` finding) for harmful requests. + **Custom HTTP target.** When the user points at their own multimodal endpoint (a URL) and hands you docs or a link, READ the docs, infer the request/response shape + auth, and build a `custom_request_template` using the `{prompt}` / `{image_b64}` / `{audio_b64}` / `{video_b64}` diff --git a/capabilities/ai-red-teaming/capability.yaml b/capabilities/ai-red-teaming/capability.yaml index b1abe46..85b285a 100644 --- a/capabilities/ai-red-teaming/capability.yaml +++ b/capabilities/ai-red-teaming/capability.yaml @@ -1,6 +1,6 @@ schema: 1 name: ai-red-teaming -version: "1.6.8" +version: "1.6.9" description: > Probe the security and safety of AI applications, agents, and foundation models. Orchestrates adversarial attack workflows to discover vulnerabilities in LLMs, diff --git a/capabilities/ai-red-teaming/scripts/attack_runner.py b/capabilities/ai-red-teaming/scripts/attack_runner.py index a90c37f..08465e2 100644 --- a/capabilities/ai-red-teaming/scripts/attack_runner.py +++ b/capabilities/ai-red-teaming/scripts/attack_runner.py @@ -5384,6 +5384,29 @@ def _build_custom_multimodal_target(custom: dict) -> str: return "\n".join(lines) +def _build_streaming_multimodal_target(custom: dict) -> str: + """Build a streaming speech-to-speech target via the SDK's ``nova_sonic_target``. + + Amazon Nova Sonic is a bidirectional S2S model: it takes the input audio part and + returns a Message with the model's spoken reply (audio) + transcript. The SDK task + plugs straight into ``multimodal_attack`` like any other target. Requires AWS + credentials in the environment (env vars / role / platform secrets) and Bedrock + access to Nova Sonic in the region — no API key. + """ + protocol = custom.get("protocol", "nova_sonic") + if protocol != "nova_sonic": + raise ValueError("Unsupported streaming protocol: {!r}".format(protocol)) + region = _safe_str(custom.get("region") or "us-east-1") + voice = _safe_str(custom.get("voice") or "matthew") + system_prompt = _safe_str(custom.get("system_prompt") or "You are a helpful voice assistant.") + model_id = _safe_str(custom.get("model_id") or "amazon.nova-sonic-v1:0") + return ( + "from dreadnode.airt import nova_sonic_target\n" + 'target = nova_sonic_target(region="{}", voice="{}", ' + 'system_prompt="{}", model_id="{}")\n'.format(region, voice, system_prompt, model_id) + ) + + def _build_multimodal_target(custom: dict | None = None) -> str: """A @task target that sends a multimodal Message to the target. @@ -5394,6 +5417,8 @@ def _build_multimodal_target(custom: dict | None = None) -> str: text content, so text-out targets behave exactly as before. """ if custom: + if custom.get("transport") == "streaming": + return _build_streaming_multimodal_target(custom) return _build_custom_multimodal_target(custom) return """\ @task @@ -5491,6 +5516,7 @@ def _load(paths, cls): airt_assessment_id=assessment.assessment_id, airt_goal_category=GOAL_CATEGORY.value, airt_target_model=TARGET_MODEL, + airt_evaluator_model=JUDGE_MODEL, ) await attack.run() except Exception as e: @@ -5536,8 +5562,25 @@ def generate_multimodal_attack(params: dict) -> dict: # Custom HTTP endpoint target: point the attack at *any* multimodal endpoint by # URL instead of a litellm model. Mirrors generate_attack's custom-target block. custom_url = (params.get("custom_url") or "").strip() + custom_transport = (params.get("custom_transport") or "http").strip() + custom_protocol = (params.get("custom_protocol") or "").strip() custom_target = None - if custom_url: + if custom_transport == "streaming" and custom_protocol: + # Streaming speech-to-speech target (e.g. Amazon Nova Sonic). No HTTP URL — + # the SDK adapter drives the bidirectional stream; auth is the cloud's own. + model_id = params.get("custom_model_id") or "amazon.nova-sonic-v1:0" + custom_target = { + "transport": "streaming", + "protocol": custom_protocol, + "region": params.get("custom_region") or "us-east-1", + "voice": params.get("custom_voice") or "matthew", + "system_prompt": params.get("custom_system_prompt") + or "You are a helpful voice assistant.", + "model_id": model_id, + } + if not target_model: + target_model = model_id # display/provenance only; the adapter is the target + elif custom_url: custom_target = { "url": custom_url, "auth_type": params.get("custom_auth_type", "none"), @@ -5550,10 +5593,11 @@ def generate_multimodal_attack(params: dict) -> dict: if not goal: return {"error": "goal is required (the text prompt to send with the media)"} - if not target_model and not custom_url: + if not target_model and not custom_target: return { "error": "target_model is required (a vision/audio-capable model), " - "or provide custom_url for a custom HTTP endpoint" + "or provide custom_url for a custom HTTP endpoint, or " + "custom_transport='streaming' + custom_protocol='nova_sonic' for a speech-to-speech target" } # For a custom endpoint with no target_model, TARGET_MODEL only backs a diff --git a/capabilities/ai-red-teaming/tools/attacks.py b/capabilities/ai-red-teaming/tools/attacks.py index 193ef72..0a90a1d 100644 --- a/capabilities/ai-red-teaming/tools/attacks.py +++ b/capabilities/ai-red-teaming/tools/attacks.py @@ -462,6 +462,23 @@ def generate_multimodal_attack( "JSON request template with {prompt}/{image_b64}/{audio_b64}/{video_b64} placeholders.", ] = "", custom_response_text_path: t.Annotated[str, "JSONPath to the response text (e.g. $.response)"] = "", + custom_transport: t.Annotated[ + str, + "Target transport: 'http' (default) or 'streaming' for a realtime speech-to-speech " + "target. With 'streaming', set custom_protocol (no custom_url needed).", + ] = "http", + custom_protocol: t.Annotated[ + str, + "Streaming protocol when custom_transport='streaming'. Supported: 'nova_sonic' " + "(Amazon Nova Sonic S2S over Bedrock). The input audio is spoken to the model and " + "its spoken reply (audio + transcript) is scored.", + ] = "", + custom_region: t.Annotated[str, "AWS region for a streaming target (default us-east-1)."] = "", + custom_voice: t.Annotated[str, "Voice id for a Nova Sonic streaming target (default matthew)."] = "", + custom_system_prompt: t.Annotated[str, "System prompt for a streaming S2S target."] = "", + custom_model_id: t.Annotated[ + str, "Model id for a streaming target (default amazon.nova-sonic-v1:0)." + ] = "", score_media_output: t.Annotated[ bool, "Score the target's GENERATED media (image-out / speech-to-speech), not just its " @@ -546,6 +563,18 @@ def generate_multimodal_attack( params["custom_request_template"] = custom_request_template if custom_response_text_path: params["custom_response_text_path"] = custom_response_text_path + if custom_transport and custom_transport != "http": + params["custom_transport"] = custom_transport + if custom_protocol: + params["custom_protocol"] = custom_protocol + if custom_region: + params["custom_region"] = custom_region + if custom_voice: + params["custom_voice"] = custom_voice + if custom_system_prompt: + params["custom_system_prompt"] = custom_system_prompt + if custom_model_id: + params["custom_model_id"] = custom_model_id if score_media_output: params["score_media_output"] = True if media_output_modalities: