dreadnode 1.0.0__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,125 @@
1
+ Metadata-Version: 2.3
2
+ Name: dreadnode
3
+ Version: 1.0.0
4
+ Summary: Dreadnode SDK
5
+ Author: Nick Landers
6
+ Author-email: monoxgas@gmail.com
7
+ Requires-Python: >=3.10,<3.14
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
+ Classifier: Programming Language :: Python :: 3.13
13
+ Provides-Extra: training
14
+ Requires-Dist: coolname (>=2.2.0,<3.0.0)
15
+ Requires-Dist: fast-depends (>=2.4.12,<3.0.0)
16
+ Requires-Dist: fsspec[s3] (>=2023.1.0,<=2025.3.0)
17
+ Requires-Dist: httpx (>=0.28.0,<0.29.0)
18
+ Requires-Dist: logfire (>=3.5.3,<4.0.0)
19
+ Requires-Dist: pandas (>=2.2.3,<3.0.0)
20
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
21
+ Requires-Dist: python-ulid (>=3.0.0,<4.0.0)
22
+ Requires-Dist: transformers (>=4.41.0,<5.0.0) ; extra == "training"
23
+ Project-URL: Repository, https://github.com/dreadnode/sdk
24
+ Description-Content-Type: text/markdown
25
+
26
+ <p align="center">
27
+ <img
28
+ src="https://d1lppblt9t2x15.cloudfront.net/logos/5714928f3cdc09503751580cffbe8d02.png"
29
+ alt="Logo"
30
+ align="center"
31
+ width="144px"
32
+ height="144px"
33
+ />
34
+ </p>
35
+
36
+ <h3 align="center">
37
+ Dreadnode Strikes SDK
38
+ </h3>
39
+
40
+ <h4 align="center">
41
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dreadnode">
42
+ <img alt="PyPI - Version" src="https://img.shields.io/pypi/v/dreadnode">
43
+ <img alt="GitHub License" src="https://img.shields.io/github/license/dreadnode/sdk">
44
+ <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/tests.yaml">
45
+ <img alt="Pre-Commit" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/pre-commit.yaml">
46
+ <img alt="Renovate" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/renovate.yaml">
47
+ </h4>
48
+
49
+ </br>
50
+
51
+ Strikes is a platform for building, experimenting with, and evaluating AI security agent code.
52
+
53
+ - **Experiment + Tasking + Observability** in a single place that's lightweight and scales.
54
+ - **Track your data** with parameters, inputs, and outputs all connected to your tasks.
55
+ - **Log your artifacts** — data, models, files, and folders — to track data of your Dreadnode runs, enabling easy reuse and reproducibility.
56
+ - **Measure everything** with metrics throughout your code and anywhere you need them.
57
+ - **Scale your code** from a single run to thousands.
58
+
59
+ ```python
60
+ import dreadnode as dn
61
+ import rigging as rg
62
+
63
+ from .tools import reversing_tools
64
+
65
+ dn.configure()
66
+
67
+ @dataclass
68
+ class Finding:
69
+ name: str
70
+ severity: str
71
+ description: str
72
+ exploit_code: str
73
+
74
+ @dn.scorer(name="Score Finding")
75
+ async def score_finding(finding: Finding) -> float:
76
+ if finding.severity == "critical":
77
+ return 1.0
78
+ elif finding.severity == "high":
79
+ return 0.8
80
+ else:
81
+ return 0.2
82
+
83
+ @dn.task(scorers=[score_finding])
84
+ @rg.prompt(tools=[reversing_tools])
85
+ async def analyze_binary(binary: str) -> list[Finding]:
86
+ """
87
+ Analyze the binary for vulnerabilities.
88
+ """
89
+ ...
90
+
91
+ with dn.run(tags=["reverse-engineering"]):
92
+ binary = "c2/downloads/service.exe"
93
+
94
+ dn.log_params(
95
+ model="gpt-4",
96
+ temperature=0.5,
97
+ binary=binary
98
+ )
99
+
100
+ findings = await analyze_binary(binary)
101
+
102
+ dn.log_metric("findings", len(findings))
103
+ ```
104
+
105
+ ## Installation
106
+
107
+ We publish every version to PyPi:
108
+ ```bash
109
+ pip install -U dreadnode
110
+ ```
111
+
112
+ If you want to build from source:
113
+ ```bash
114
+ poetry install
115
+ ```
116
+
117
+ See our **[installation guide](https://docs.dreadnode.io/strikes/install)** for more options.
118
+
119
+ ## Getting Started
120
+
121
+ Read through our **[introduction guide](https://docs.dreadnode.io/strikes/intro)** in the docs.
122
+
123
+ ## Examples
124
+
125
+ 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=bdP57EiCn9ZRU0uCzvosrlKbaDB42m9nvWFa6dDjwnk,8213
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=Ql4dc-uI5KcqaBjxL_qPx7Au8f1zKLUAsXiKlkXH0nw,35166
13
+ dreadnode/metric.py,sha256=8AUGoXyL119N52gq8WbUc9muK7rHvcEUHgej_07toYw,7231
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=ZBZCO1yFv6jQEgvlaA5_tLKExnCJnstzyRrvZyJvfyc,25457
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.0.dist-info/METADATA,sha256=Tzdq_td8YpVjzpZLaJ5Y_tMQ_NHcva6z95D8S8tOemo,3793
26
+ dreadnode-1.0.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
27
+ dreadnode-1.0.0.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