dreadnode 1.16.0__tar.gz → 1.17.0__tar.gz

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.
Files changed (227) hide show
  1. {dreadnode-1.16.0 → dreadnode-1.17.0}/.gitignore +1 -0
  2. {dreadnode-1.16.0 → dreadnode-1.17.0}/PKG-INFO +12 -4
  3. {dreadnode-1.16.0 → dreadnode-1.17.0}/README.md +1 -1
  4. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/agent.py +11 -3
  5. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/base.py +68 -2
  6. dreadnode-1.17.0/dreadnode/agent/tools/fs.py +867 -0
  7. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/__init__.py +2 -0
  8. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/base.py +3 -0
  9. dreadnode-1.17.0/dreadnode/airt/attack/crescendo.py +221 -0
  10. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/goat.py +116 -14
  11. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/prompt.py +44 -22
  12. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/tap.py +7 -2
  13. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/target/llm.py +37 -13
  14. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/api/client.py +18 -11
  15. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/api/models.py +1 -1
  16. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/constants.py +18 -0
  17. dreadnode-1.17.0/dreadnode/data/assets/adversarial_benchmark_subset.csv +71 -0
  18. dreadnode-1.17.0/dreadnode/data/assets/ai_safety.csv +81 -0
  19. dreadnode-1.17.0/dreadnode/data/assets/bomb.jpg +0 -0
  20. dreadnode-1.17.0/dreadnode/data/assets/meth.png +0 -0
  21. dreadnode-1.17.0/dreadnode/data/templates/crescendo/variant_1.yaml +69 -0
  22. dreadnode-1.17.0/dreadnode/data/templates/crescendo/variant_2.yaml +43 -0
  23. dreadnode-1.17.0/dreadnode/data/templates/crescendo/variant_3.yaml +24 -0
  24. dreadnode-1.17.0/dreadnode/data/templates/crescendo/variant_4.yaml +49 -0
  25. dreadnode-1.17.0/dreadnode/data/templates/crescendo/variant_5.yaml +56 -0
  26. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/image.py +2 -2
  27. dreadnode-1.17.0/dreadnode/data_types/message.py +229 -0
  28. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/text.py +1 -1
  29. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/console.py +1 -1
  30. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/eval.py +137 -37
  31. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/events.py +19 -0
  32. dreadnode-1.17.0/dreadnode/eval/hooks/__init__.py +13 -0
  33. dreadnode-1.17.0/dreadnode/eval/hooks/base.py +26 -0
  34. dreadnode-1.17.0/dreadnode/eval/hooks/transforms.py +104 -0
  35. dreadnode-1.17.0/dreadnode/eval/reactions.py +35 -0
  36. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/sample.py +2 -1
  37. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/logging_.py +6 -1
  38. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/main.py +12 -7
  39. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/console.py +51 -10
  40. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/stop.py +18 -13
  41. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/study.py +96 -71
  42. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/trial.py +9 -0
  43. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/tracing/span.py +4 -2
  44. dreadnode-1.17.0/dreadnode/transforms/cipher.py +625 -0
  45. dreadnode-1.17.0/dreadnode/transforms/encoding.py +598 -0
  46. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/image.py +95 -0
  47. dreadnode-1.17.0/dreadnode/transforms/multimodal.py +155 -0
  48. dreadnode-1.17.0/dreadnode/transforms/perturbation.py +1492 -0
  49. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/refine.py +59 -18
  50. dreadnode-1.17.0/dreadnode/transforms/text.py +599 -0
  51. dreadnode-1.17.0/examples/airt/ai_red_teaming_eval.ipynb +1898 -0
  52. dreadnode-1.17.0/examples/airt/crescendo_attack.ipynb +162 -0
  53. dreadnode-1.17.0/examples/airt/graph_of_attacks_with_pruning.ipynb +181 -0
  54. dreadnode-1.17.0/examples/airt/multimodal_attack_eval.ipynb +263 -0
  55. dreadnode-1.17.0/examples/airt/tap_vs_goat_eval.ipynb +549 -0
  56. dreadnode-1.17.0/examples/airt/tree_of_attacks_with_pruning.ipynb +183 -0
  57. dreadnode-1.17.0/examples/airt/tree_of_attacks_with_pruning_transforms.ipynb +186 -0
  58. {dreadnode-1.16.0 → dreadnode-1.17.0}/pyproject.toml +14 -8
  59. dreadnode-1.17.0/tests/test_agent_lifecycle.py +239 -0
  60. dreadnode-1.16.0/dreadnode/agent/tools/fs.py +0 -395
  61. dreadnode-1.16.0/dreadnode/transforms/cipher.py +0 -68
  62. dreadnode-1.16.0/dreadnode/transforms/encoding.py +0 -79
  63. dreadnode-1.16.0/dreadnode/transforms/perturbation.py +0 -307
  64. dreadnode-1.16.0/dreadnode/transforms/text.py +0 -158
  65. dreadnode-1.16.0/examples/airt/graph_of_attacks_with_pruning.ipynb +0 -179
  66. dreadnode-1.16.0/examples/airt/tap_vs_goat_eval.ipynb +0 -2303
  67. dreadnode-1.16.0/examples/airt/tree_of_attacks_with_pruning.ipynb +0 -181
  68. {dreadnode-1.16.0 → dreadnode-1.17.0}/LICENSE +0 -0
  69. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/__init__.py +0 -0
  70. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/__main__.py +0 -0
  71. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/__init__.py +0 -0
  72. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/error.py +0 -0
  73. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/events.py +0 -0
  74. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/format.py +0 -0
  75. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/hooks/__init__.py +0 -0
  76. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/hooks/backoff.py +0 -0
  77. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/hooks/base.py +0 -0
  78. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/hooks/metrics.py +0 -0
  79. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/hooks/summarize.py +0 -0
  80. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/prompts/__init__.py +0 -0
  81. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/prompts/summarize.py +0 -0
  82. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/reactions.py +0 -0
  83. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/result.py +0 -0
  84. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/stop.py +0 -0
  85. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/thread.py +0 -0
  86. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/__init__.py +0 -0
  87. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/execute.py +0 -0
  88. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/memory.py +0 -0
  89. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/planning.py +0 -0
  90. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/reporting.py +0 -0
  91. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/agent/tools/tasking.py +0 -0
  92. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/__init__.py +0 -0
  93. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/hop_skip_jump.py +0 -0
  94. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/nes.py +0 -0
  95. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/simba.py +0 -0
  96. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/attack/zoo.py +0 -0
  97. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/__init__.py +0 -0
  98. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/hop_skip_jump.py +0 -0
  99. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/image_utils.py +0 -0
  100. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/nes.py +0 -0
  101. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/simba.py +0 -0
  102. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/search/zoo.py +0 -0
  103. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/target/__init__.py +0 -0
  104. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/target/base.py +0 -0
  105. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/airt/target/custom.py +0 -0
  106. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/api/__init__.py +0 -0
  107. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/api/util.py +0 -0
  108. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/artifact/__init__.py +0 -0
  109. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/artifact/credential_manager.py +0 -0
  110. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/artifact/merger.py +0 -0
  111. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/artifact/storage.py +0 -0
  112. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/artifact/tree_builder.py +0 -0
  113. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/__init__.py +0 -0
  114. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/agent/__init__.py +0 -0
  115. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/agent/cli.py +0 -0
  116. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/api.py +0 -0
  117. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/attack/__init__.py +0 -0
  118. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/attack/cli.py +0 -0
  119. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/docker.py +0 -0
  120. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/eval/__init__.py +0 -0
  121. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/eval/cli.py +0 -0
  122. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/github.py +0 -0
  123. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/main.py +0 -0
  124. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/__init__.py +0 -0
  125. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/cli.py +0 -0
  126. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/compose.py +0 -0
  127. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/constants.py +0 -0
  128. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/download.py +0 -0
  129. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/env_mgmt.py +0 -0
  130. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/tag.py +0 -0
  131. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/platform/version.py +0 -0
  132. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/profile/__init__.py +0 -0
  133. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/profile/cli.py +0 -0
  134. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/rbac/__init__.py +0 -0
  135. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/rbac/organizations.py +0 -0
  136. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/rbac/workspaces.py +0 -0
  137. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/shared.py +0 -0
  138. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/study/__init__.py +0 -0
  139. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/study/cli.py +0 -0
  140. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/task/__init__.py +0 -0
  141. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/cli/task/cli.py +0 -0
  142. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/common_types.py +0 -0
  143. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/convert.py +0 -0
  144. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/__init__.py +0 -0
  145. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/audio.py +0 -0
  146. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/base.py +0 -0
  147. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/object_3d.py +0 -0
  148. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/table.py +0 -0
  149. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/data_types/video.py +0 -0
  150. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/discovery.py +0 -0
  151. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/error.py +0 -0
  152. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/__init__.py +0 -0
  153. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/dataset.py +0 -0
  154. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/format.py +0 -0
  155. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/eval/result.py +0 -0
  156. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/exporter.py +0 -0
  157. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/format.py +0 -0
  158. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/integrations/__init__.py +0 -0
  159. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/integrations/transformers.py +0 -0
  160. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/meta/__init__.py +0 -0
  161. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/meta/config.py +0 -0
  162. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/meta/context.py +0 -0
  163. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/meta/hydrate.py +0 -0
  164. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/meta/introspect.py +0 -0
  165. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/metric.py +0 -0
  166. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/object.py +0 -0
  167. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/__init__.py +0 -0
  168. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/collectors.py +0 -0
  169. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/events.py +0 -0
  170. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/format.py +0 -0
  171. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/result.py +0 -0
  172. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/sampling.py +0 -0
  173. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/__init__.py +0 -0
  174. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/base.py +0 -0
  175. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/boundary.py +0 -0
  176. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/graph.py +0 -0
  177. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/optuna_.py +0 -0
  178. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/optimization/search/random.py +0 -0
  179. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/py.typed +0 -0
  180. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/__init__.py +0 -0
  181. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/base.py +0 -0
  182. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/classification.py +0 -0
  183. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/consistency.py +0 -0
  184. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/contains.py +0 -0
  185. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/crucible.py +0 -0
  186. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/format.py +0 -0
  187. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/harm.py +0 -0
  188. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/image.py +0 -0
  189. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/json.py +0 -0
  190. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/judge.py +0 -0
  191. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/length.py +0 -0
  192. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/lexical.py +0 -0
  193. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/pii.py +0 -0
  194. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/readability.py +0 -0
  195. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/rigging.py +0 -0
  196. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/sentiment.py +0 -0
  197. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/similarity.py +0 -0
  198. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/scorers/util.py +0 -0
  199. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/serialization.py +0 -0
  200. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/task.py +0 -0
  201. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/tracing/__init__.py +0 -0
  202. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/tracing/constants.py +0 -0
  203. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/tracing/exporters.py +0 -0
  204. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/__init__.py +0 -0
  205. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/base.py +0 -0
  206. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/stylistic.py +0 -0
  207. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/substitution.py +0 -0
  208. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/transforms/swap.py +0 -0
  209. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/user_config.py +0 -0
  210. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/util.py +0 -0
  211. {dreadnode-1.16.0 → dreadnode-1.17.0}/dreadnode/version.py +0 -0
  212. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/airt/beam_search.ipynb +0 -0
  213. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/data_export.ipynb +0 -0
  214. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_artifact.ipynb +0 -0
  215. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_object/audio.ipynb +0 -0
  216. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_object/image.ipynb +0 -0
  217. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_object/object3d.ipynb +0 -0
  218. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_object/table.ipynb +0 -0
  219. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/log_object/video.ipynb +0 -0
  220. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/model_training.ipynb +0 -0
  221. {dreadnode-1.16.0 → dreadnode-1.17.0}/examples/rigging.ipynb +0 -0
  222. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/cli/test_config.py +0 -0
  223. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/cli/test_docker.py +0 -0
  224. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/cli/test_github.py +0 -0
  225. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/test_agent.py +0 -0
  226. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/test_meta.py +0 -0
  227. {dreadnode-1.16.0 → dreadnode-1.17.0}/tests/test_task_output_linking.py +0 -0
@@ -156,6 +156,7 @@ venv.bak/
156
156
 
157
157
  # mkdocs documentation
158
158
  /site
159
+ debug.html
159
160
 
160
161
  # mypy
161
162
  .mypy_cache/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dreadnode
3
- Version: 1.16.0
3
+ Version: 1.17.0
4
4
  Summary: Dreadnode SDK
5
5
  Project-URL: Homepage, https://github.com/dreadnode/sdk
6
6
  Project-URL: Repository, https://github.com/dreadnode/sdk
@@ -208,14 +208,22 @@ License: Apache License
208
208
  See the License for the specific language governing permissions and
209
209
  limitations under the License.
210
210
  License-File: LICENSE
211
+ Classifier: License :: OSI Approved :: Apache Software License
212
+ Classifier: Operating System :: OS Independent
213
+ Classifier: Programming Language :: Python :: 3
214
+ Classifier: Programming Language :: Python :: 3.10
215
+ Classifier: Programming Language :: Python :: 3.11
216
+ Classifier: Programming Language :: Python :: 3.12
217
+ Classifier: Programming Language :: Python :: 3.13
211
218
  Requires-Python: <3.14,>=3.10
219
+ Requires-Dist: aiofiles<25.0.0,>=24.1.0
212
220
  Requires-Dist: coolname<3.0.0,>=2.2.0
213
221
  Requires-Dist: cyclopts>=4.2.0
214
- Requires-Dist: fsspec[s3]<=2025.3.0,>=2023.1.0
222
+ Requires-Dist: fsspec[s3]<=2025.12.0,>=2023.1.0
215
223
  Requires-Dist: httpx<1.0.0,>=0.28.0
216
224
  Requires-Dist: logfire<=3.20.0,>=3.5.3
217
225
  Requires-Dist: loguru>=0.7.3
218
- Requires-Dist: numpy<=2.2.6
226
+ Requires-Dist: numpy<=2.3.5
219
227
  Requires-Dist: optuna<5.0.0,>=4.5.0
220
228
  Requires-Dist: pandas<3.0.0,>=2.2.3
221
229
  Requires-Dist: pydantic<3.0.0,>=2.9.2
@@ -278,7 +286,7 @@ Dreadnode Strikes SDK
278
286
  <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dreadnode">
279
287
  <img alt="PyPI - Version" src="https://img.shields.io/pypi/v/dreadnode">
280
288
  <img alt="GitHub License" src="https://img.shields.io/github/license/dreadnode/sdk">
281
- <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/tests.yaml">
289
+ <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/test.yaml">
282
290
  <img alt="Pre-Commit" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/pre-commit.yaml">
283
291
  <img alt="Renovate" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/renovate.yaml">
284
292
  </h4>
@@ -16,7 +16,7 @@ Dreadnode Strikes SDK
16
16
  <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dreadnode">
17
17
  <img alt="PyPI - Version" src="https://img.shields.io/pypi/v/dreadnode">
18
18
  <img alt="GitHub License" src="https://img.shields.io/github/license/dreadnode/sdk">
19
- <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/tests.yaml">
19
+ <img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/test.yaml">
20
20
  <img alt="Pre-Commit" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/pre-commit.yaml">
21
21
  <img alt="Renovate" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/renovate.yaml">
22
22
  </h4>
@@ -2,7 +2,7 @@ import inspect
2
2
  import json
3
3
  import re
4
4
  import typing as t
5
- from contextlib import aclosing, asynccontextmanager
5
+ from contextlib import AsyncExitStack, aclosing, asynccontextmanager
6
6
  from copy import deepcopy
7
7
  from textwrap import dedent
8
8
 
@@ -875,8 +875,16 @@ class Agent(Model):
875
875
  commit: CommitBehavior = "always",
876
876
  ) -> t.AsyncIterator[t.AsyncGenerator[AgentEvent, None]]:
877
877
  thread = thread or self.thread
878
- async with aclosing(self._stream_traced(thread, user_input, commit=commit)) as stream:
879
- yield stream
878
+
879
+ async with AsyncExitStack() as stack:
880
+ # Ensure all tools are properly entered if they
881
+ # are context managers before we start using them
882
+ for tool_container in self.tools:
883
+ if hasattr(tool_container, "__aenter__") and hasattr(tool_container, "__aexit__"):
884
+ await stack.enter_async_context(tool_container)
885
+
886
+ async with aclosing(self._stream_traced(thread, user_input, commit=commit)) as stream:
887
+ yield stream
880
888
 
881
889
  async def run(
882
890
  self,
@@ -1,6 +1,8 @@
1
+ import asyncio
2
+ import functools
1
3
  import typing as t
2
4
 
3
- from pydantic import ConfigDict
5
+ from pydantic import ConfigDict, PrivateAttr
4
6
  from rigging import tools
5
7
  from rigging.tools.base import ToolMethod as RiggingToolMethod
6
8
 
@@ -171,18 +173,82 @@ class Toolset(Model):
171
173
  - Pydantic's declarative syntax for defining state (fields).
172
174
  - Automatic application of the `@configurable` decorator.
173
175
  - A `get_tools` method for discovering methods decorated with `@dreadnode.tool_method`.
176
+ - Support for async context management, with automatic re-entrancy handling.
174
177
  """
175
178
 
179
+ model_config = ConfigDict(arbitrary_types_allowed=True, use_attribute_docstrings=True)
180
+
176
181
  variant: str | None = None
177
182
  """The variant for filtering tools available in this toolset."""
178
183
 
179
- model_config = ConfigDict(arbitrary_types_allowed=True, use_attribute_docstrings=True)
184
+ # Context manager magic
185
+ _entry_ref_count: int = PrivateAttr(default=0)
186
+ _context_handle: object = PrivateAttr(default=None)
187
+ _entry_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
180
188
 
181
189
  @property
182
190
  def name(self) -> str:
183
191
  """The name of the toolset, derived from the class name."""
184
192
  return self.__class__.__name__
185
193
 
194
+ def __init_subclass__(cls, **kwargs: t.Any) -> None:
195
+ super().__init_subclass__(**kwargs)
196
+
197
+ # This essentially ensures that if the Toolset is any kind of context manager,
198
+ # it will be re-entrant, and only actually enter/exit once. This means we can
199
+ # safely build auto-entry/exit logic into our Agent class without worrying about
200
+ # breaking the code if the user happens to enter a toolset manually before using
201
+ # it in an agent.
202
+
203
+ original_aenter = cls.__dict__.get("__aenter__")
204
+ original_enter = cls.__dict__.get("__enter__")
205
+ original_aexit = cls.__dict__.get("__aexit__")
206
+ original_exit = cls.__dict__.get("__exit__")
207
+
208
+ has_enter = callable(original_aenter) or callable(original_enter)
209
+ has_exit = callable(original_aexit) or callable(original_exit)
210
+
211
+ if has_enter and not has_exit:
212
+ raise TypeError(
213
+ f"{cls.__name__} defining __aenter__ or __enter__ must also define __aexit__ or __exit__"
214
+ )
215
+ if has_exit and not has_enter:
216
+ raise TypeError(
217
+ f"{cls.__name__} defining __aexit__ or __exit__ must also define __aenter__ or __enter__"
218
+ )
219
+ if original_aenter and original_enter:
220
+ raise TypeError(f"{cls.__name__} cannot define both __aenter__ and __enter__")
221
+ if original_aexit and original_exit:
222
+ raise TypeError(f"{cls.__name__} cannot define both __aexit__ and __exit__")
223
+
224
+ @functools.wraps(original_aenter or original_enter) # type: ignore[arg-type]
225
+ async def aenter_wrapper(self: "Toolset", *args: t.Any, **kwargs: t.Any) -> t.Any:
226
+ async with self._entry_lock:
227
+ if self._entry_ref_count == 0:
228
+ handle = None
229
+ if original_aenter:
230
+ handle = await original_aenter(self, *args, **kwargs)
231
+ elif original_enter:
232
+ handle = original_enter(self, *args, **kwargs)
233
+ self._context_handle = handle if handle is not None else self
234
+ self._entry_ref_count += 1
235
+ return self._context_handle
236
+
237
+ cls.__aenter__ = aenter_wrapper # type: ignore[attr-defined]
238
+
239
+ @functools.wraps(original_aexit or original_exit) # type: ignore[arg-type]
240
+ async def aexit_wrapper(self: "Toolset", *args: t.Any, **kwargs: t.Any) -> t.Any:
241
+ async with self._entry_lock:
242
+ self._entry_ref_count -= 1
243
+ if self._entry_ref_count == 0:
244
+ if original_aexit:
245
+ await original_aexit(self, *args, **kwargs)
246
+ elif original_exit:
247
+ original_exit(self, *args, **kwargs)
248
+ self._context_handle = None
249
+
250
+ cls.__aexit__ = aexit_wrapper # type: ignore[attr-defined]
251
+
186
252
  def get_tools(self, *, variant: str | None = None) -> list[AnyTool]:
187
253
  variant = variant or self.variant
188
254