mirror of
https://github.com/416rehman/DeepZero-Agentic-Vulnerability-Research-Pipeline
synced 2026-08-09 11:55:01 +00:00
A run left its output spread across work/<pipeline>/samples/<id>/ as per-sample state, findings, decompiled sources and assessments. Reviewing thousands of those by hand is not practical, so `deepzero run` now prints a link to a report and keeps it up to date as results land, and `deepzero report` rebuilds it at any time. The report answers one question first: what is vulnerable. Items an assessment stage marked vulnerable lead the page, then items with findings but no confirmed verdict, then anything that errored. Each item links to its own page carrying the assessment, every finding with the code it matched, and links to the artifacts on disk. It is built from what a pipeline actually recorded rather than from any one domain's field names, so a source-code review over repositories renders as well as a kernel-driver review. A pipeline can shape the presentation with an optional `report:` block - what to call one item, which stage data key holds the verdict, which values mean vulnerable, and which columns to surface - and every field has a default. Output is layered so it stays usable on a large corpus: index.html holds the triage summary at a bounded size, items/<id>.html covers everything worth reading, inventory.csv carries every item for a spreadsheet, and findings.jsonl carries every finding one per line. When a listing is capped the page says what was capped and where the rest is. Pages are self-contained with no network access, readable in light and dark, keyboard navigable, and escape all analysed content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
from typing import Any
|
|
|
|
from deepzero.engine.backends import create_backend
|
|
|
|
log = logging.getLogger("deepzero.llm")
|
|
|
|
|
|
class LLMProvider:
|
|
# llm provider with adaptive retry and backoff.
|
|
#
|
|
# the backend is resolved from the model string by the backend registry
|
|
# (see deepzero.engine.backends). this class knows nothing about any
|
|
# specific backend - it only drives the retry roles each one declares.
|
|
|
|
def __init__(self, model: str, **kwargs: Any):
|
|
self.model = model
|
|
self.default_kwargs = kwargs
|
|
self.backend = create_backend(model, **kwargs)
|
|
|
|
@property
|
|
def _litellm(self) -> Any:
|
|
# retained for backward compatibility with existing callers/tests
|
|
return getattr(self.backend, "litellm", None)
|
|
|
|
def complete(
|
|
self,
|
|
messages: list[dict[str, str]],
|
|
max_retries: int = 3,
|
|
initial_backoff: float = 2.0,
|
|
max_backoff: float = 60.0,
|
|
backoff_decay: float = 0.7,
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""send messages to the llm and return the response text.
|
|
handles rate limiting with adaptive backoff."""
|
|
backend = self.backend
|
|
# every option is forwarded and each backend takes what applies to it:
|
|
# litellm passes generation kwargs to the api, while cli backends read
|
|
# controls such as timeout and ignore the rest
|
|
merged = {**self.default_kwargs, **kwargs}
|
|
backoff = initial_backoff
|
|
|
|
for attempt in range(max_retries + 1):
|
|
try:
|
|
content = backend.raw_complete(messages, **merged)
|
|
|
|
# decay backoff toward minimum on success
|
|
backoff = max(initial_backoff, backoff * backoff_decay)
|
|
return content
|
|
|
|
except backend.rate_limit_error:
|
|
if attempt == max_retries:
|
|
raise
|
|
backoff = min(max_backoff, backoff * 2.0)
|
|
log.warning(
|
|
"rate limited (attempt %d/%d), backing off %.0fs",
|
|
attempt + 1,
|
|
max_retries + 1,
|
|
backoff,
|
|
)
|
|
time.sleep(backoff)
|
|
|
|
except backend.context_window_error:
|
|
# context window errors won't be fixed by retry
|
|
raise
|
|
|
|
except backend.non_retryable_errors:
|
|
# e.g. auth failures - surface immediately instead of burning retries
|
|
raise
|
|
|
|
except backend.retryable_errors as e:
|
|
if attempt == max_retries:
|
|
raise
|
|
wait = min(max_backoff, 2**attempt)
|
|
log.warning(
|
|
"llm error (attempt %d/%d), retrying in %.0fs: %s",
|
|
attempt + 1,
|
|
max_retries + 1,
|
|
wait,
|
|
e,
|
|
)
|
|
time.sleep(wait)
|
|
|
|
raise RuntimeError("exhausted retries without raising")
|
|
|
|
def check_auth(self) -> str | None:
|
|
"""opt-in readiness probe; delegates to the backend. returns None if
|
|
ready, else a human-readable reason."""
|
|
return self.backend.check_auth()
|
|
|
|
@property
|
|
def provider_name(self) -> str:
|
|
return self.backend.provider_name
|
|
|
|
@property
|
|
def model_name(self) -> str:
|
|
return self.backend.model_name
|