Previously, calculate_cost() was always called without a model parameter,
causing all scans to report costs based on deepseek-chat pricing regardless
of the actual target model (e.g. gpt-4, claude-3-opus).
Changes:
- http_spec.py: Add 'model_name' property to LLMSpec that extracts the
model field from the JSON request body. Returns 'unknown' if the body
is not valid JSON or has no 'model' field.
- probe_data/image_generator.py: Add 'model_name' pass-through property
to RequestAdapter, delegating to the underlying LLMSpec.
- probe_data/audio_generator.py: Same as above - add 'model_name'
pass-through property to RequestAdapter.
- probe_actor/cost_module.py:
- Change return type from float to float | None.
- Unknown models now log a warning and return None instead of raising
ValueError, so scans are not interrupted by unsupported model names.
- Add logger import for the warning message.
- probe_actor/fuzzer.py: Pass model_name to calculate_cost() in both
scan_module() and perform_many_shot_scan() using
getattr(request_factory, 'model_name', 'unknown').
- primitives/models.py: Update ScanResult.cost type from float to
float | None to accommodate unknown model pricing.
msj_data.py contained a full copy of the ProbeDataset dataclass that
was already defined canonically in probe_data/models.py, violating DRY
and leaving a stale TODO comment in the source.
Changes:
- probe_data/msj_data.py: delete the 19-line duplicate ProbeDataset
definition and the now-unused 'from dataclasses import dataclass'
import; replace with a single re-export:
from agentic_security.probe_data.models import ProbeDataset
All call-sites inside the file (load_dataset_generic, prepare_prompts)
continue to work unchanged because the field signatures are identical.
The TODO comment is removed as the refactor is now complete.
No changes required in consumers (fuzzer.py, test_msj_data.py) because
they access ProbeDataset through msj_data's re-export.
The /scan-csv endpoint was reading the uploaded CSV file but discarding
the content (TODO comment), resulting in scans that ran with zero prompts.
Changes:
- routes/scan.py: parse uploaded CSV via parse_csv_content(), pass the
extracted prompts as inline_datasets to the Scan model; also fix the
maxBudget query parameter being silently ignored (hardcoded to 1000).
- probe_data/data.py: add parse_csv_content(bytes) -> ProbeDataset that
looks for a 'prompt' column first, falls back to the first text column,
and raises ValueError when no suitable column is found.
- primitives/models.py: add inline_datasets: list[dict] field to Scan
model for carrying uploaded prompts through the scan pipeline.
- probe_actor/fuzzer.py: perform_single_shot_scan now accepts
inline_datasets and appends them as ProbeDataset objects to the scan
modules; scan_router transparently forwards the field.
Removes Content-Length from request headers before sending with httpx
to prevent LocalProtocolError when placeholder replacement (e.g.
<<PROMPT>>) changes the body size. httpx calculates the correct
Content-Length from the actual content.
Closes#139
Closes#193
Expands the MCP server section with:
- what tools are exposed and what each one does
- step-by-step Claude Desktop setup
- the three built-in prompt templates and when to use them
- a short example conversation showing natural-language scan control
- Claude Code CLI setup for terminal-based workflows
Closes#192
Three prompt templates via @mcp.prompt():
- security_scan_prompt: full scan with configurable probe budget
- verify_llm_prompt: quick reachability check before committing to a scan
- adversarial_probe_prompt: multi-step attack session with findings summary
Placed before the tool definitions with a clear section comment.
No existing tool behaviour changed.