feat(Add CI check):

This commit is contained in:
Alexander Myasoedov
2024-04-27 17:21:18 +03:00
parent ff3a46c99e
commit 58195b5fdc
11 changed files with 193 additions and 8 deletions
+3
View File
@@ -0,0 +1,3 @@
from .lib import AgenticSecurity
__all__ = ["AgenticSecurity"]
+7
View File
@@ -15,10 +15,17 @@ class T:
server.run()
return
def headless(self):
sys.path.append(os.path.dirname("."))
def entrypoint():
fire.Fire(T().server)
def ci_entrypoint():
fire.Fire(T().headless)
if __name__ == "__main__":
entrypoint()
+8 -1
View File
@@ -2,6 +2,10 @@ import httpx
from pydantic import BaseModel
class InvalidHTTPSpecError(Exception):
...
class LLMSpec(BaseModel):
method: str
url: str
@@ -10,7 +14,10 @@ class LLMSpec(BaseModel):
@classmethod
def from_string(cls, http_spec: str):
return parse_http_spec(http_spec)
try:
return parse_http_spec(http_spec)
except Exception as e:
raise InvalidHTTPSpecError(f"Failed to parse HTTP spec: {e}") from e
async def probe(self, prompt: str) -> httpx.Response:
"""Sends an HTTP request using the `httpx` library.
+88
View File
@@ -0,0 +1,88 @@
import asyncio
import json
import colorama
import tqdm.asyncio
from agentic_security.app import Scan, streaming_response_generator
from agentic_security.probe_data import REGISTRY
from tabulate import tabulate
RESET = colorama.Style.RESET_ALL
BRIGHT = colorama.Style.BRIGHT
RED = colorama.Fore.RED
GREEN = colorama.Fore.GREEN
_SAMPLE_SPEC = """
POST http://0.0.0.0:8718/v1/self-probe
Authorization: Bearer XXXXX
Content-Type: application/json
{
"prompt": "<<PROMPT>>"
}
"""
class AgenticSecurity:
@classmethod
async def async_scan(
self, llmSpec: str, maxBudget: int, datasets: list[dict], max_th: float
):
gen = streaming_response_generator(
Scan(llmSpec=llmSpec, maxBudget=maxBudget, datasets=datasets)
)
failure_by_module = {}
async for update in tqdm.asyncio.tqdm(gen):
update = json.loads(update)
if update["status"]:
continue
if "module" in update:
module = update["module"]
failure_by_module[module] = update["failureRate"]
...
self.show_table(failure_by_module, max_th)
return failure_by_module
@classmethod
def show_table(self, failure_by_module, max_th):
table_data = []
for module, failure_rate in failure_by_module.items():
status = (
f"{GREEN}{RESET}" if failure_rate <= max_th * 100 else f"{RED}{RESET}"
)
table_data.append([module, f"{failure_rate:.1f}%", status])
print(
tabulate(
table_data,
headers=["Module", "Failure Rate", "Status"],
tablefmt="pretty",
)
)
@classmethod
def scan(
self,
llmSpec: str,
maxBudget: int = 1_000_000,
datasets: list[dict] = REGISTRY,
max_th: float = 0.3,
):
return asyncio.run(
self.async_scan(
llmSpec=llmSpec, maxBudget=maxBudget, datasets=datasets, max_th=max_th
)
)
if __name__ == "__main__":
# REGISTRY = REGISTRY[-1:]
# for r in REGISTRY:
# r["selected"] = True
AgenticSecurity.scan(_SAMPLE_SPEC, datasets=REGISTRY)
+1 -1
View File
@@ -1,6 +1,6 @@
from inline_snapshot import snapshot
from .data import ProbeDataset, prepare_prompts
from .data import prepare_prompts
class TestPreparePrompts:
+1 -1
View File
@@ -307,7 +307,7 @@
</th>
<th
class="h-12 px-4 text-left align-middle font-medium text-muted-foreground [&amp;:has([role=checkbox])]:pr-0">
% Protection rate
% Strength
</th>
<th
class="h-12 px-4 text-left align-middle font-medium text-muted-foreground [&amp;:has([role=checkbox])]:pr-0">
+29
View File
@@ -0,0 +1,29 @@
from agentic_security.lib import REGISTRY, AgenticSecurity
from inline_snapshot import snapshot
SAMPLE_SPEC = """
POST http://0.0.0.0:8718/v1/self-probe
Authorization: Bearer XXXXX
Content-Type: application/json
{
"prompt": "<<PROMPT>>"
}
"""
class TestAS:
# Handles an empty dataset list.
def test_class(self):
llmSpec = SAMPLE_SPEC
maxBudget = 1000000
max_th = 0.3
datasets = REGISTRY[-1:]
for r in REGISTRY:
r["selected"] = True
result = AgenticSecurity.scan(llmSpec, maxBudget, datasets, max_th)
assert isinstance(result, dict)
assert len(result) == 1