feat(Report chart):

This commit is contained in:
Alexander Myasoedov
2024-04-26 13:51:17 +03:00
parent 5855139d9c
commit 8182d5efec
5 changed files with 124 additions and 22 deletions
+12 -1
View File
@@ -3,7 +3,7 @@ import sys
from datetime import datetime
from pathlib import Path
from fastapi import BackgroundTasks, FastAPI, HTTPException
from fastapi import BackgroundTasks, FastAPI, HTTPException, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, StreamingResponse
from loguru import logger
@@ -13,6 +13,7 @@ from .http_spec import LLMSpec
from .probe_actor import fuzzer
from .probe_actor.refusal import REFUSAL_MARKS
from .probe_data import REGISTRY
from .report_chart import plot_security_report
logger.remove(0)
logger.add(
@@ -138,3 +139,13 @@ async def failures_csv():
if not Path("failures.csv").exists():
return {"error": "No failures found"}
return FileResponse("failures.csv")
class Table(BaseModel):
table: list[dict]
@app.post("/plot.jpeg", response_class=Response)
async def get_plot(table: Table):
buf = plot_security_report(table.table)
return StreamingResponse(buf, media_type="image/jpeg")
+1 -2
View File
@@ -31,8 +31,7 @@ REFUSAL_MARKS = [
def check_refusal(response: str, refusal_phrases: list = REFUSAL_MARKS) -> bool:
"""
Check if the response contains any phrases indicating refusal to answer.
"""Check if the response contains any phrases indicating refusal to answer.
Args:
- response (str): The response from the language model.
+74
View File
@@ -0,0 +1,74 @@
from io import BytesIO
from textwrap import wrap
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.cm import ScalarMappable
def plot_security_report(table):
data = pd.DataFrame(table)
# Sorting by failureRate for a meaningful arrangement
data_sorted = data.sort_values("failureRate", ascending=False)
# Values for the plot
angles = np.linspace(0, 2 * np.pi, len(data_sorted), endpoint=False)
failure_rate = data_sorted["failureRate"]
tokens = data_sorted["tokens"]
# Styling parameters
COLORS = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"]
cmap = mpl.colors.LinearSegmentedColormap.from_list("custom", COLORS, N=256)
norm = mpl.colors.Normalize(vmin=tokens.min(), vmax=tokens.max())
# Polar plot setup
fig, ax = plt.subplots(figsize=(10, 8), subplot_kw={"projection": "polar"})
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.set_facecolor("white")
# Bars for failureRate with colors based on 'tokens'
bars = ax.bar(
angles,
failure_rate,
width=0.3,
color=[cmap(norm(t)) for t in tokens],
alpha=0.75,
label="Failure Rate %",
)
# Add labels for the modules
module_labels = ["\n".join(wrap(m, 10)) for m in data_sorted["module"]]
ax.set_xticks(angles)
# Add dashed vertical lines. These are just references
ax.set_xticklabels(module_labels, fontsize=7, color="#333")
# Color bar for the tokens
sm = ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, orientation="horizontal", pad=0.1)
cbar.set_label("Token Count (k)", fontsize=12, color="#444")
# Grid and legend
ax.grid(True, color="gray", linestyle=":", linewidth=0.5)
plt.legend(loc="upper right", bbox_to_anchor=(1.1, 1.1))
ax.vlines(angles, 0, 100, color="#444", ls=(0, (4, 4)), zorder=11)
# Title and subtitle
title = "Security Report for Different Modules"
# fig.suptitle(title, fontsize=18, weight="bold", ha="center", va="top")
caption = "Report generated by https://github.com/msoedov/agentic_security"
fig.text(0.5, 0.025, caption, fontsize=10, ha="center", va="baseline")
buf = BytesIO()
plt.savefig(buf, format="jpeg")
plt.close(fig)
buf.seek(0)
return buf
+23
View File
@@ -286,6 +286,7 @@
v-bind:style="{width: progressWidth}">
</div>
<img :src="imageUrl" alt="Generated Plot">
<div
class="rounded-lg border bg-card text-card-foreground shadow-sm"
data-v0-t="card">
@@ -419,6 +420,7 @@ Content-Type: application/json
errorMsg: '',
maskMode: false,
okMsg: '',
reportImageUrl: '',
selectedConfig: 0,
configs: [
{ name: 'Custom API', prompts: 40000, customInstructions: 'Requires api spec' },
@@ -549,10 +551,31 @@ Content-Type: application/json
last.last = false;
this.mainTable.push(event);
event.last = true;
// this.newRow()
}
this.okMsg = `New event: ${event.module}: ${event.progress}%`;
},
newRow: async function() {
console.log('New row');
let payload = {
table: this.mainTable,
};
const response = await fetch(`${URL}/plot.jpeg`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
// Convert image response to a data URL for the <img> src
const blob = await response.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
this.reportImageUrl = reader.result;
};
},
startScan: async function() {
let payload = {
maxBudget: this.budget,