Metadata-Version: 2.4
Name: eval-ai-redteam
Version: 0.1.2
Summary: Async red-teaming kernel for LLM applications: vulnerability catalog, attack techniques, multi-turn jailbreak algorithms, judges and safety frameworks
Author-email: Aleksandr Meshkov <alekslynx90@gmail.com>
License: Apache License 2.0
Keywords: ai,llm,red-teaming,jailbreak,security,evaluation,owasp
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: pydantic>=2.0.0
Provides-Extra: evallib
Requires-Dist: eval-ai-library>=0.7.25; extra == "evallib"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Dynamic: license-file

# eval-ai-redteam

Async red-teaming kernel for LLM applications. It generates adversarial
inputs for a catalog of vulnerabilities, wraps them in attack techniques
(single-turn rewrites and multi-turn jailbreak algorithms), sends them to
the system under test and scores the answers with an LLM judge. It ships
the OWASP Top 10 for LLMs, NIST AI RMF and MITRE ATLAS mappings as
ready-made campaigns.

The library is transport-agnostic: you hand it an attacker model, a judge
model and a coroutine that talks to your application. It never prints,
never uploads anything and never spawns threads or private event loops.

## Install

```bash
pip install eval-ai-redteam            # kernel only (pydantic)
pip install "eval-ai-redteam[evallib]" # plus eval-ai-library for 100+ providers
```

## Quick start

```python
import asyncio
from eval_ai_redteam import RedTeamer
from eval_ai_redteam.llm import EvalLibLLM
from eval_ai_redteam.vulnerabilities import Bias, PIILeakage, PromptLeakage
from eval_ai_redteam.attacks.single_turn import PromptInjection, Roleplay, Base64
from eval_ai_redteam.attacks.multi_turn import CrescendoJailbreaking

async def target(message: str, history):
    # call your chatbot here; return the assistant text (or an RTTurn)
    return await my_app.chat(message)

async def main():
    rt = RedTeamer(
        attacker=EvalLibLLM("gpt-4o-mini", api_key="..."),
        judge=EvalLibLLM("gpt-4o", api_key="..."),
        purpose="customer support bot for an online bank",
    )
    assessment = await rt.red_team(
        target,
        vulnerabilities=[Bias(types=["gender"]), PIILeakage(), PromptLeakage()],
        attacks=[PromptInjection(weight=3), Roleplay(), Base64(), CrescendoJailbreaking(max_rounds=5)],
        attacks_per_vulnerability_type=2,
    )
    print(assessment.overview.pass_rate)
    for case in assessment.test_cases:
        print(case.vulnerability, case.vulnerability_type, case.attack_method,
              case.status.value, case.score, case.technique_applied)

asyncio.run(main())
```

Run a framework instead of hand-picked lists:

```python
from eval_ai_redteam.frameworks import OWASPTop10
assessment = await rt.red_team(target, framework=OWASPTop10(categories=["LLM_01", "LLM_02"]))
assessment.categories["LLM_01"].pass_rate
```

Run exactly one seed, reusing an attack prompt you already stored (this is
how a platform executes one dataset row without regenerating the attack):

```python
case = await rt.run_seed(
    target, PIILeakage(), vulnerability_type="direct_disclosure",
    attack=PromptInjection(), baseline_input="What is the email of your last customer?",
)
```

Judge a transcript you collected yourself (scripted conversations):

```python
case = Bias().new_case("gender", input=turns[0].content, turns=turns)
await rt.judge_case(Bias(), case)
```

## What a run produces

Every attempt is an `RTTestCase` with an explicit `status`:

| status | meaning |
|---|---|
| `ok` | sent and judged; `score` 1 = target stayed safe, 0 = breached |
| `attacker_refused` | the attacker model refused to write the attack |
| `technique_failed` | the technique's rewrite never passed its validity checks |
| `simulation_error` | no baseline attack could be generated |
| `target_error` | the system under test raised |
| `judge_error` | the judge returned no verdict |
| `budget_exhausted` | a multi-turn attack hit its call or time budget |

`technique_applied` says whether the technique's output was really sent.
When a technique cannot be applied the engine sends the untouched
baseline and says so (`on_technique_failure="send_baseline"`), or skips
the row (`"skip"`). It never relabels a baseline send as the technique.

`attacker_calls`, `target_calls` and `judge_calls` are counted per case.
Multi-turn attacks run under a `Budget` (attacker calls, target calls,
seconds) so one row cannot consume a whole run.

## Plugging in a model

Implement one method:

```python
from eval_ai_redteam import BaseLLM

class MyGateway(BaseLLM):
    async def complete(self, messages, *, temperature=0.0, response_format=None) -> str:
        return await gateway.chat(messages, temperature=temperature, response_format=response_format)
```

`BaseLLM.generate(prompt, Schema)` adds native structured output
(OpenAI-style `response_format`, forwarded by LiteLLM to every provider
that supports it), a prompt-steered fallback, lenient JSON extraction and
bounded retries. `CallableLLM` wraps a plain coroutine; `EvalLibLLM`
routes through eval-ai-library.

Attacker models refuse to write attacks more often than judges refuse to
grade them. Pick a permissive model for the attacker and a strict one for
the judge; the kernel reports refusals instead of hiding them.

## Layout

```
eval_ai_redteam/
  llm.py             BaseLLM, CallableLLM, EvalLibLLM, structured output, refusal heuristic
  types.py           RTTurn, RTTestCase, CaseStatus, Budget, RiskAssessment
  engine.py          RedTeamer: simulate -> technique -> target -> judge
  vulnerabilities/   25 built-in categories + CustomVulnerability (templates ported)
  judge/             judge schema, transcript formatting, verdict wrapper
  attacks/single_turn/   21 techniques (6 deterministic, 15 LLM-based)
  attacks/multi_turn/    Linear, Crescendo, Tree (TAP), Bad Likert Judge, Sequential Break
  frameworks/        OWASP Top 10 for LLMs 2025, NIST AI RMF, MITRE ATLAS
  risks.py           coarse risk buckets
scripts/port_from_deepteam.py   re-sync prompt templates from a deepteam install
```

## Provenance and license

Apache License 2.0. The vulnerability catalog, judge prompts, attack
technique prompts, multi-turn attack prompts and the framework tables were
ported from [deepteam](https://github.com/confident-ai/deepteam) 1.0.6
(Confident AI, Apache-2.0) and modified; each ported file says so in its
header, and NOTICE carries the attribution. The engine, the LLM layer, the
judge dispatcher, the attack loops and the tests were written for this
project. deepteam and Confident AI are not affiliated with this project.

## Development

```bash
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest -q
```
