dreadnode 1.0.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dreadnode/types.py ADDED
@@ -0,0 +1,25 @@
1
+ import typing as t
2
+
3
+ # Common types
4
+
5
+ JsonValue = t.Union[
6
+ int,
7
+ float,
8
+ str,
9
+ bool,
10
+ None,
11
+ list["JsonValue"],
12
+ tuple["JsonValue", ...],
13
+ "JsonDict",
14
+ ]
15
+ JsonDict = dict[str, JsonValue]
16
+
17
+ AnyDict = dict[str, t.Any]
18
+
19
+
20
+ class Unset:
21
+ def __bool__(self) -> t.Literal[False]:
22
+ return False
23
+
24
+
25
+ UNSET: Unset = Unset()
dreadnode/util.py ADDED
@@ -0,0 +1,150 @@
1
+ # Lots of utilities shamelessly copied from the `logfire` package.
2
+ # https://github.com/pydantic/logfire
3
+
4
+ import inspect
5
+ import logging
6
+ import os
7
+ import sys
8
+ import typing as t
9
+ from contextlib import contextmanager
10
+ from pathlib import Path
11
+ from types import TracebackType
12
+
13
+ from logfire import suppress_instrumentation
14
+ from logfire._internal.stack_info import add_non_user_code_prefix, is_user_code
15
+
16
+ import dreadnode
17
+
18
+ SysExcInfo = (
19
+ tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]
20
+ )
21
+ """
22
+ The return type of sys.exc_info(): exc_type, exc_val, exc_tb.
23
+ """
24
+
25
+ logger = logging.getLogger("dreadnode")
26
+
27
+ add_non_user_code_prefix(Path(dreadnode.__file__).parent)
28
+
29
+
30
+ def safe_repr(obj: t.Any) -> str:
31
+ """
32
+ Return some kind of non-empty string representation of an object, catching exceptions.
33
+ """
34
+
35
+ try:
36
+ result = repr(obj)
37
+ except Exception: # noqa: BLE001
38
+ result = ""
39
+
40
+ if result:
41
+ return result
42
+
43
+ try:
44
+ return f"<{type(obj).__name__} object>"
45
+ except Exception: # noqa: BLE001
46
+ return "<unknown (repr failed)>"
47
+
48
+
49
+ def log_internal_error() -> None:
50
+ try:
51
+ current_test = os.environ.get("PYTEST_CURRENT_TEST", "")
52
+ reraise = bool(current_test and "test_internal_exception" not in current_test)
53
+ except Exception: # noqa: BLE001
54
+ reraise = False
55
+
56
+ if reraise:
57
+ raise # noqa: PLE0704
58
+
59
+ with suppress_instrumentation(): # prevent infinite recursion from the logging integration
60
+ logger.exception(
61
+ "Caught an error in Dreadnode. This will not prevent code from running, but you may lose data.",
62
+ exc_info=_internal_error_exc_info(),
63
+ )
64
+
65
+
66
+ def _internal_error_exc_info() -> SysExcInfo:
67
+ """Returns an exc_info tuple with a nicely tweaked traceback."""
68
+ original_exc_info: SysExcInfo = sys.exc_info()
69
+ exc_type, exc_val, original_tb = original_exc_info
70
+ try:
71
+ # First remove redundant frames already in the traceback about where the error was raised.
72
+ tb = original_tb
73
+ if tb and tb.tb_frame and tb.tb_frame.f_code is _HANDLE_INTERNAL_ERRORS_CODE:
74
+ # Skip the 'yield' line in _handle_internal_errors
75
+ tb = tb.tb_next
76
+
77
+ if (
78
+ tb
79
+ and tb.tb_frame
80
+ and tb.tb_frame.f_code.co_filename == contextmanager.__code__.co_filename
81
+ and tb.tb_frame.f_code.co_name == "inner"
82
+ ):
83
+ # Skip the 'inner' function frame when handle_internal_errors is used as a decorator.
84
+ # It looks like `return func(*args, **kwds)`
85
+ tb = tb.tb_next
86
+
87
+ # Now add useful outer frames that give context, but skipping frames that are just about handling the error.
88
+ frame = inspect.currentframe()
89
+ # Skip this frame right here.
90
+ assert frame # noqa: S101
91
+ frame = frame.f_back
92
+
93
+ if frame and frame.f_code is log_internal_error.__code__: # pragma: no branch
94
+ # This function is always called from log_internal_error, so skip that frame.
95
+ frame = frame.f_back
96
+ assert frame # noqa: S101
97
+
98
+ if frame.f_code is _HANDLE_INTERNAL_ERRORS_CODE:
99
+ # Skip the line in _handle_internal_errors that calls log_internal_error
100
+ frame = frame.f_back
101
+ # Skip the frame defining the _handle_internal_errors context manager
102
+ assert frame # noqa: S101
103
+ assert frame.f_code.co_name == "__exit__" # noqa: S101
104
+ frame = frame.f_back
105
+ assert frame # noqa: S101
106
+ # Skip the frame calling the context manager, on the `with` line.
107
+ frame = frame.f_back
108
+ else:
109
+ # `log_internal_error()` was called directly, so just skip that frame. No context manager stuff.
110
+ frame = frame.f_back
111
+
112
+ # Now add all remaining frames from internal logfire code.
113
+ while frame and not is_user_code(frame.f_code):
114
+ tb = TracebackType(
115
+ tb_next=tb,
116
+ tb_frame=frame,
117
+ tb_lasti=frame.f_lasti,
118
+ tb_lineno=frame.f_lineno,
119
+ )
120
+ frame = frame.f_back
121
+
122
+ # Add up to 3 frames from user code.
123
+ for _ in range(3):
124
+ if not frame: # pragma: no cover
125
+ break
126
+ tb = TracebackType(
127
+ tb_next=tb,
128
+ tb_frame=frame,
129
+ tb_lasti=frame.f_lasti,
130
+ tb_lineno=frame.f_lineno,
131
+ )
132
+ frame = frame.f_back
133
+
134
+ assert exc_type # noqa: S101
135
+ assert exc_val # noqa: S101
136
+ exc_val = exc_val.with_traceback(tb)
137
+ return exc_type, exc_val, tb # noqa: TRY300
138
+ except Exception: # noqa: BLE001
139
+ return original_exc_info
140
+
141
+
142
+ @contextmanager
143
+ def handle_internal_errors() -> t.Iterator[None]:
144
+ try:
145
+ yield
146
+ except Exception: # noqa: BLE001
147
+ log_internal_error()
148
+
149
+
150
+ _HANDLE_INTERNAL_ERRORS_CODE = inspect.unwrap(handle_internal_errors).__code__
dreadnode/version.py ADDED
@@ -0,0 +1,3 @@
1
+ import importlib_metadata
2
+
3
+ VERSION = importlib_metadata.version("dreadnode")
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.3
2
+ Name: dreadnode
3
+ Version: 1.0.0rc0
4
+ Summary: Dreadnode SDK
5
+ Author: Nick Landers
6
+ Author-email: monoxgas@gmail.com
7
+ Requires-Python: >=3.10,<3.13
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Provides-Extra: training
13
+ Requires-Dist: coolname (>=2.2.0,<3.0.0)
14
+ Requires-Dist: fast-depends (>=2.4.12,<3.0.0)
15
+ Requires-Dist: fsspec[s3] (>=2023.1.0,<=2025.3.0)
16
+ Requires-Dist: httpx (>=0.28.0,<0.29.0)
17
+ Requires-Dist: logfire (>=3.5.3,<4.0.0)
18
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
19
+ Requires-Dist: python-ulid (>=3.0.0,<4.0.0)
20
+ Requires-Dist: transformers (>=4.41.0,<5.0.0) ; extra == "training"
21
+ Project-URL: Repository, https://github.com/dreadnode/sdk
22
+ Description-Content-Type: text/markdown
23
+
24
+ <p align="center">
25
+ <img
26
+ src="https://d1lppblt9t2x15.cloudfront.net/logos/5714928f3cdc09503751580cffbe8d02.png"
27
+ alt="Logo"
28
+ align="center"
29
+ width="144px"
30
+ height="144px"
31
+ />
32
+ </p>
33
+
34
+ <h3 align="center">
35
+ Dreadnode Strikes SDK
36
+ </h3>
37
+
38
+ <h4 align="center">
39
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dreadnode">
40
+ <img alt="PyPI - Version" src="https://img.shields.io/pypi/v/dreadnode">
41
+ <img alt="GitHub License" src="https://img.shields.io/github/license/dreadnode/sdk">
42
+ <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/tests.yaml">
43
+ <img alt="Pre-Commit" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/pre-commit.yaml">
44
+ <img alt="Renovate" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/renovate.yaml">
45
+ </h4>
46
+
47
+ </br>
48
+
49
+ Strikes is an platform for building, experimenting with, and evaluating AI security agent code.
50
+
51
+ - **Experiment + Tasking + Observability** in a single place that's lightweight and scales.
52
+ - **Track your data** with parameters, inputs, and outputs all connected to your tasks.
53
+ - **Measure everything** with metrics throughout your code and anywhere you need them.
54
+ - **Scale your code** from a single run to thousands.
55
+
56
+ ```python
57
+ import dreadnode as dn
58
+ import rigging as rg
59
+
60
+ from .tools import reversing_tools
61
+
62
+ dn.configure()
63
+
64
+ @dataclass
65
+ class Finding:
66
+ name: str
67
+ severity: str
68
+ description: str
69
+ exploit_code: str
70
+
71
+ @dn.scorer(name="Score Finding")
72
+ async def score_finding(finding: Finding) -> float:
73
+ if finding.severity == "critical":
74
+ return 1.0
75
+ elif finding.severity == "high":
76
+ return 0.8
77
+ else:
78
+ return 0.2
79
+
80
+ @dn.task(scorers=[score_finding])
81
+ @rg.prompt(tools=[reversing_tools])
82
+ async def analyze_binary(binary: str) -> list[Finding]:
83
+ """
84
+ Analyze the binary for vulnerabilities.
85
+ """
86
+ ...
87
+
88
+ with dn.run(tags=["reverse-engineering"]):
89
+ binary = "c2/downloads/service.exe"
90
+
91
+ dn.log_params(
92
+ model="gpt-4",
93
+ temperature=0.5,
94
+ binary=binary
95
+ )
96
+
97
+ findings = await analyze_binary(binary)
98
+
99
+ dn.log_metric("findings", len(findings))
100
+ ```
101
+
102
+ ## Installation
103
+
104
+ We publish every version to PyPi:
105
+ ```bash
106
+ pip install -U dreadnode
107
+ ```
108
+
109
+ If you want to build from source:
110
+ ```bash
111
+ poetry install
112
+ ```
113
+
114
+ See our **[installation guide](https://docs.dreadnode.io/strikes/install)** for more options.
115
+
116
+ ## Getting Started
117
+
118
+ Read through our **[introduction guide](https://docs.dreadnode.io/strikes/intro)** in the docs.
119
+
120
+ ## Examples
121
+
122
+ Check out **[dreadnode/example-agents](https://github.com/dreadnode/example-agents)** to find your favorite use case.
@@ -0,0 +1,27 @@
1
+ dreadnode/__init__.py,sha256=MCe5IZTposO-rigZPBGHkyLqHkIZe-ItA2WxNWMQUQU,1269
2
+ dreadnode/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ dreadnode/api/client.py,sha256=nlwH_JHgPJjfZg_KUCwX9Nu6gQgUmzmM7cFE98AwaSY,8401
4
+ dreadnode/api/models.py,sha256=qdCU0gJg2ZCl3Abe1zsf5rqiaKaLOE_Fxj5jxIAZXWo,3922
5
+ dreadnode/artifact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ dreadnode/artifact/merger.py,sha256=DQXvia8Q3ukM7CkLSCjqJcoVN7gpam77Uw_XioXZgD4,21524
7
+ dreadnode/artifact/storage.py,sha256=0OPSZdZTfLZaaq7BkRbf7uj4hN2DV9UHF4pNqsN0tX8,4012
8
+ dreadnode/artifact/tree_builder.py,sha256=GrCxu8H2OXeyx7vCU6Fw-Qs1lC0uV8IAOYq912ZXF_E,16071
9
+ dreadnode/constants.py,sha256=UY-zRo1H_VpWW0cYEXyROtgIRoEGdo2QJNUDjCac0fY,518
10
+ dreadnode/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ dreadnode/integrations/transformers.py,sha256=rzkUBf232NFWiJ2wTtXMSdpdItKWdxSF7IXMm5bHDEo,5253
12
+ dreadnode/main.py,sha256=uhechSIlKMU0f6PwXSWAyCh22TDUQWrizOg--37aek8,34938
13
+ dreadnode/metric.py,sha256=B51WLIORQdXlWEeJ3AjE1F5ylkQ3_dUFIT0mQeeXqH8,7069
14
+ dreadnode/object.py,sha256=b6B8cWI2nN-DXH_FbC_DtlNwGJ_HIzvO6DiYed-0Ru0,403
15
+ dreadnode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ dreadnode/serialization.py,sha256=CuZGikN02mBVc83XYXV03wYjiYEtMScQhearcSgYJ6Q,20992
17
+ dreadnode/task.py,sha256=lrspiTA4ZzTRTV2PxRjQI8-AAAt2iQW-ILKzYVgH584,15016
18
+ dreadnode/tracing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ dreadnode/tracing/constants.py,sha256=ObmPeM_VLZzSEfkcUwinq4B4C3713PjzS6REawZWphA,1587
20
+ dreadnode/tracing/exporters.py,sha256=1OTumIb4k1JDjJfAmTxr7u7xhoB1rh9hiS8-5RN1NcA,4960
21
+ dreadnode/tracing/span.py,sha256=pGuJIj2W4Nc6DtFMs7zRYJ2qFRw6kmmJbzNx6eXXo28,25064
22
+ dreadnode/types.py,sha256=DD3r5ZtufouIMwWUsMQwDgVhMpjPwsR40O6wsQKIsxA,341
23
+ dreadnode/util.py,sha256=Nwh-Idyr_I-bRYmO3WQmEbNmjNCjfJZuWX1m2NiCTD4,5088
24
+ dreadnode/version.py,sha256=kytZz5e7pAGE4YzT0F9mDC6nSNIwwGGrNXovH0S6LkQ,77
25
+ dreadnode-1.0.0rc0.dist-info/METADATA,sha256=82gjFkvfETJnaUn25Ivy0N6-HSdKiwkulZCxSRhkIeQ,3562
26
+ dreadnode-1.0.0rc0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
27
+ dreadnode-1.0.0rc0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.2
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any