hackagent 0.4.1__tar.gz → 0.4.3__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 (215) hide show
  1. {hackagent-0.4.1 → hackagent-0.4.3}/PKG-INFO +6 -1
  2. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/orchestrator.py +67 -3
  3. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/attack.py +149 -0
  4. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/completions.py +48 -37
  5. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/evaluation.py +10 -2
  6. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/evaluators.py +134 -31
  7. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/generate.py +84 -43
  8. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/base.py +3 -2
  9. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/baseline/evaluation.py +138 -8
  10. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/baseline/generation.py +105 -31
  11. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/pair/attack.py +187 -42
  12. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/commands/agent.py +1 -1
  13. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/commands/attack.py +24 -5
  14. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/attacks.py +1 -0
  15. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/config.py +37 -1
  16. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/widgets/logs.py +32 -5
  17. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/utils.py +1 -0
  18. hackagent-0.4.3/hackagent/datasets/__init__.py +63 -0
  19. hackagent-0.4.3/hackagent/datasets/base.py +99 -0
  20. hackagent-0.4.3/hackagent/datasets/presets.py +223 -0
  21. hackagent-0.4.3/hackagent/datasets/providers/__init__.py +23 -0
  22. hackagent-0.4.3/hackagent/datasets/providers/file.py +236 -0
  23. hackagent-0.4.3/hackagent/datasets/providers/huggingface.py +183 -0
  24. hackagent-0.4.3/hackagent/datasets/registry.py +247 -0
  25. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/__init__.py +6 -4
  26. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/adapters/__init__.py +31 -11
  27. hackagent-0.4.3/hackagent/router/adapters/base.py +662 -0
  28. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/adapters/google_adk.py +42 -50
  29. hackagent-0.4.1/hackagent/router/adapters/litellm_adapter.py → hackagent-0.4.3/hackagent/router/adapters/litellm.py +115 -204
  30. hackagent-0.4.3/hackagent/router/adapters/ollama.py +510 -0
  31. hackagent-0.4.1/hackagent/router/adapters/openai_adapter.py → hackagent-0.4.3/hackagent/router/adapters/openai.py +95 -217
  32. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/router.py +216 -12
  33. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/tracking/__init__.py +15 -2
  34. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/tracking/decorators.py +1 -1
  35. hackagent-0.4.1/hackagent/router/tracking/tracker.py → hackagent-0.4.3/hackagent/router/tracking/step.py +141 -9
  36. hackagent-0.4.3/hackagent/router/tracking/tracker.py +646 -0
  37. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/types.py +5 -0
  38. {hackagent-0.4.1 → hackagent-0.4.3}/pyproject.toml +12 -3
  39. hackagent-0.4.1/hackagent/router/adapters/base.py +0 -63
  40. {hackagent-0.4.1 → hackagent-0.4.3}/.gitignore +0 -0
  41. {hackagent-0.4.1 → hackagent-0.4.3}/LICENSE +0 -0
  42. {hackagent-0.4.1 → hackagent-0.4.3}/README.md +0 -0
  43. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/__init__.py +0 -0
  44. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/agent.py +0 -0
  45. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/__init__.py +0 -0
  46. {hackagent-0.4.1/hackagent/api/user → hackagent-0.4.3/hackagent/api/agent}/__init__.py +0 -0
  47. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_create.py +0 -0
  48. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_destroy.py +0 -0
  49. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_list.py +0 -0
  50. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_partial_update.py +0 -0
  51. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_retrieve.py +0 -0
  52. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/agent/agent_update.py +0 -0
  53. {hackagent-0.4.1/hackagent/api/run → hackagent-0.4.3/hackagent/api/apilogs}/__init__.py +0 -0
  54. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/apilogs/apilogs_list.py +0 -0
  55. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/apilogs/apilogs_retrieve.py +0 -0
  56. {hackagent-0.4.1/hackagent/api/result → hackagent-0.4.3/hackagent/api/attack}/__init__.py +0 -0
  57. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_create.py +0 -0
  58. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_destroy.py +0 -0
  59. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_list.py +0 -0
  60. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_partial_update.py +0 -0
  61. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_retrieve.py +0 -0
  62. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/attack/attack_update.py +0 -0
  63. {hackagent-0.4.1/hackagent/api/prompt → hackagent-0.4.3/hackagent/api/checkout}/__init__.py +0 -0
  64. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/checkout/checkout_create.py +0 -0
  65. {hackagent-0.4.1/hackagent/api/organization → hackagent-0.4.3/hackagent/api/generate}/__init__.py +0 -0
  66. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/generate/generate_create.py +0 -0
  67. {hackagent-0.4.1/hackagent/api/key → hackagent-0.4.3/hackagent/api/judge}/__init__.py +0 -0
  68. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/judge/judge_create.py +0 -0
  69. {hackagent-0.4.1/hackagent/api/judge → hackagent-0.4.3/hackagent/api/key}/__init__.py +0 -0
  70. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/key/key_create.py +0 -0
  71. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/key/key_destroy.py +0 -0
  72. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/key/key_list.py +0 -0
  73. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/key/key_retrieve.py +0 -0
  74. {hackagent-0.4.1/hackagent/api/generate → hackagent-0.4.3/hackagent/api/organization}/__init__.py +0 -0
  75. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_create.py +0 -0
  76. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_destroy.py +0 -0
  77. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_list.py +0 -0
  78. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_me_retrieve.py +0 -0
  79. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_partial_update.py +0 -0
  80. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_retrieve.py +0 -0
  81. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/organization/organization_update.py +0 -0
  82. {hackagent-0.4.1/hackagent/api/checkout → hackagent-0.4.3/hackagent/api/prompt}/__init__.py +0 -0
  83. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_create.py +0 -0
  84. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_destroy.py +0 -0
  85. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_list.py +0 -0
  86. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_partial_update.py +0 -0
  87. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_retrieve.py +0 -0
  88. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/prompt/prompt_update.py +0 -0
  89. {hackagent-0.4.1/hackagent/api/attack → hackagent-0.4.3/hackagent/api/result}/__init__.py +0 -0
  90. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_create.py +0 -0
  91. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_destroy.py +0 -0
  92. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_list.py +0 -0
  93. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_partial_update.py +0 -0
  94. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_retrieve.py +0 -0
  95. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_trace_create.py +0 -0
  96. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/result/result_update.py +0 -0
  97. {hackagent-0.4.1/hackagent/api/apilogs → hackagent-0.4.3/hackagent/api/run}/__init__.py +0 -0
  98. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_create.py +0 -0
  99. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_destroy.py +0 -0
  100. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_list.py +0 -0
  101. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_partial_update.py +0 -0
  102. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_result_create.py +0 -0
  103. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_retrieve.py +0 -0
  104. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_run_tests_create.py +0 -0
  105. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/run/run_update.py +0 -0
  106. {hackagent-0.4.1/hackagent/api/agent → hackagent-0.4.3/hackagent/api/user}/__init__.py +0 -0
  107. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_create.py +0 -0
  108. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_destroy.py +0 -0
  109. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_list.py +0 -0
  110. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_me_retrieve.py +0 -0
  111. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_me_update.py +0 -0
  112. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_partial_update.py +0 -0
  113. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_retrieve.py +0 -0
  114. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/api/user/user_update.py +0 -0
  115. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/__init__.py +0 -0
  116. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/base.py +0 -0
  117. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/objectives/__init__.py +0 -0
  118. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/objectives/base.py +0 -0
  119. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/objectives/harmful_behavior.py +0 -0
  120. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/objectives/jailbreak.py +0 -0
  121. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/objectives/policy_violation.py +0 -0
  122. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/registry.py +0 -0
  123. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/__init__.py +0 -0
  124. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/evaluators.py +0 -0
  125. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/metrics.py +0 -0
  126. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/progress.py +0 -0
  127. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/templates.py +0 -0
  128. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/shared/utils.py +0 -0
  129. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/__init__.py +0 -0
  130. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/__init__.py +0 -0
  131. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/config.py +0 -0
  132. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/advprefix/utils.py +0 -0
  133. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/baseline/__init__.py +0 -0
  134. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/baseline/attack.py +0 -0
  135. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/baseline/config.py +0 -0
  136. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/pair/__init__.py +0 -0
  137. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/attacks/techniques/pair/config.py +0 -0
  138. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/__init__.py +0 -0
  139. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/commands/__init__.py +0 -0
  140. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/commands/config.py +0 -0
  141. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/commands/results.py +0 -0
  142. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/config.py +0 -0
  143. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/main.py +0 -0
  144. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/__init__.py +0 -0
  145. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/actions_logger.py +0 -0
  146. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/app.py +0 -0
  147. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/base.py +0 -0
  148. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/logger.py +0 -0
  149. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/__init__.py +0 -0
  150. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/agents.py +0 -0
  151. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/dashboard.py +0 -0
  152. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/views/results.py +0 -0
  153. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/widgets/__init__.py +0 -0
  154. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/cli/tui/widgets/actions.py +0 -0
  155. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/client.py +0 -0
  156. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/errors.py +0 -0
  157. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/logger.py +0 -0
  158. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/__init__.py +0 -0
  159. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/agent.py +0 -0
  160. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/agent_request.py +0 -0
  161. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/api_token_log.py +0 -0
  162. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/attack.py +0 -0
  163. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/attack_request.py +0 -0
  164. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/checkout_session_request_request.py +0 -0
  165. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/checkout_session_response.py +0 -0
  166. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/choice.py +0 -0
  167. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/choice_message.py +0 -0
  168. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/evaluation_status_enum.py +0 -0
  169. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/generate_error_response.py +0 -0
  170. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/generate_request_request.py +0 -0
  171. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/generate_success_response.py +0 -0
  172. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/generic_error_response.py +0 -0
  173. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/message_request.py +0 -0
  174. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/organization.py +0 -0
  175. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/organization_minimal.py +0 -0
  176. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/organization_request.py +0 -0
  177. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_agent_list.py +0 -0
  178. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_api_token_log_list.py +0 -0
  179. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_attack_list.py +0 -0
  180. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_organization_list.py +0 -0
  181. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_prompt_list.py +0 -0
  182. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_result_list.py +0 -0
  183. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_run_list.py +0 -0
  184. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_user_api_key_list.py +0 -0
  185. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/paginated_user_profile_list.py +0 -0
  186. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_agent_request.py +0 -0
  187. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_attack_request.py +0 -0
  188. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_organization_request.py +0 -0
  189. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_prompt_request.py +0 -0
  190. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_result_request.py +0 -0
  191. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_run_request.py +0 -0
  192. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/patched_user_profile_request.py +0 -0
  193. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/prompt.py +0 -0
  194. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/prompt_request.py +0 -0
  195. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/result.py +0 -0
  196. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/result_list_evaluation_status.py +0 -0
  197. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/result_request.py +0 -0
  198. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/run.py +0 -0
  199. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/run_list_status.py +0 -0
  200. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/run_request.py +0 -0
  201. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/status_enum.py +0 -0
  202. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/step_type_enum.py +0 -0
  203. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/trace.py +0 -0
  204. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/trace_request.py +0 -0
  205. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/usage.py +0 -0
  206. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/user_api_key.py +0 -0
  207. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/user_api_key_request.py +0 -0
  208. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/user_profile.py +0 -0
  209. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/user_profile_minimal.py +0 -0
  210. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/models/user_profile_request.py +0 -0
  211. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/router/tracking/context.py +0 -0
  212. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/types.py +0 -0
  213. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/utils.py +0 -0
  214. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/vulnerabilities/__init__.py +0 -0
  215. {hackagent-0.4.1 → hackagent-0.4.3}/hackagent/vulnerabilities/prompts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hackagent
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: HackAgent is an open-source security toolkit to detect vulnerabilities of your AI Agents.
5
5
  Author-email: AI Security Lab <ais@ai4i.it>
6
6
  License: Apache-2.0
@@ -13,15 +13,20 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Requires-Python: >=3.10
18
+ Requires-Dist: attrs>=21.0.0
17
19
  Requires-Dist: click>=8.1.0
18
20
  Requires-Dist: litellm>=1.69.2
19
21
  Requires-Dist: openai>=1.0.0
20
22
  Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: python-dateutil>=2.8.0
21
24
  Requires-Dist: pyyaml>=6.0.0
22
25
  Requires-Dist: requests>=2.31.0
23
26
  Requires-Dist: rich>=14.0.0
24
27
  Requires-Dist: textual>=1.0.0
28
+ Provides-Extra: datasets
29
+ Requires-Dist: datasets>=2.14.0; extra == 'datasets'
25
30
  Description-Content-Type: text/markdown
26
31
 
27
32
  <div align="center">
@@ -204,22 +204,86 @@ class AttackOrchestrator:
204
204
  Extract parameters for attack execution.
205
205
 
206
206
  Override this method for custom parameter handling.
207
- Default implementation extracts 'goals' from config.
207
+ Default implementation extracts 'goals' from config, either directly
208
+ as a list or by loading them from a dataset source.
208
209
 
209
210
  Args:
210
- attack_config: Full attack configuration
211
+ attack_config: Full attack configuration. Can contain either:
212
+ - goals: Direct list of goal strings
213
+ - dataset: Configuration for loading goals from a dataset source
211
214
 
212
215
  Returns:
213
216
  Parameters to pass to technique's run() method
214
217
 
215
218
  Raises:
216
- ValueError: If required parameters are missing
219
+ ValueError: If neither 'goals' nor 'dataset' is provided, or if format is invalid
217
220
  """
221
+ # Check for direct goals first
218
222
  goals = attack_config.get("goals")
223
+ dataset_config = attack_config.get("dataset")
224
+
225
+ if goals is not None and dataset_config is not None:
226
+ logger.warning(
227
+ "Both 'goals' and 'dataset' provided. Using 'goals' directly."
228
+ )
229
+ dataset_config = None
230
+
231
+ if dataset_config is not None:
232
+ # Load goals from dataset source
233
+ goals = self._load_goals_from_dataset(dataset_config)
234
+ elif goals is None:
235
+ raise ValueError(
236
+ f"'{self.attack_type}' requires either 'goals' (list) or 'dataset' (config)"
237
+ )
238
+
219
239
  if not isinstance(goals, list):
220
240
  raise ValueError(f"'goals' must be a list for {self.attack_type}")
241
+
242
+ if len(goals) == 0:
243
+ raise ValueError(f"'goals' list is empty for {self.attack_type}")
244
+
245
+ logger.info(f"Prepared {len(goals)} goals for {self.attack_type} attack")
221
246
  return {"goals": goals}
222
247
 
248
+ def _load_goals_from_dataset(self, dataset_config: Dict[str, Any]) -> list:
249
+ """
250
+ Load goals from a dataset configuration.
251
+
252
+ Supports loading from:
253
+ - Pre-configured presets (e.g., "agentharm", "strongreject")
254
+ - HuggingFace datasets
255
+ - Local files (JSON, CSV, JSONL, TXT)
256
+
257
+ Args:
258
+ dataset_config: Dataset configuration dictionary with keys:
259
+ - preset (str, optional): Name of a pre-configured preset
260
+ - provider (str, optional): "huggingface" or "file"
261
+ - path (str, optional): Dataset path or file path
262
+ - goal_field (str, optional): Field containing goal text
263
+ - split (str, optional): Dataset split (for HuggingFace)
264
+ - limit (int, optional): Maximum goals to load
265
+ - shuffle (bool, optional): Shuffle before selecting
266
+ - seed (int, optional): Random seed for shuffling
267
+
268
+ Returns:
269
+ List of goal strings
270
+
271
+ Raises:
272
+ ValueError: If dataset configuration is invalid
273
+ ImportError: If required dependencies are not available
274
+ """
275
+ from hackagent.datasets import load_goals_from_config
276
+
277
+ logger.info(f"Loading goals from dataset: {dataset_config}")
278
+
279
+ try:
280
+ goals = load_goals_from_config(dataset_config)
281
+ logger.info(f"Loaded {len(goals)} goals from dataset")
282
+ return goals
283
+ except Exception as e:
284
+ logger.error(f"Failed to load goals from dataset: {e}", exc_info=True)
285
+ raise ValueError(f"Failed to load goals from dataset: {e}") from e
286
+
223
287
  def _get_attack_impl_kwargs(
224
288
  self,
225
289
  attack_config: Dict[str, Any],
@@ -17,6 +17,12 @@ Prefix generation pipeline attack based on the BaseAttack class.
17
17
 
18
18
  This module implements a complete pipeline for generating, filtering, and selecting prefixes
19
19
  using uncensored and target language models, adapted as an attack module.
20
+
21
+ Result Tracking:
22
+ Uses Tracker to create one Result per goal, with traces for each
23
+ prefix generation, completion, and evaluation step. This provides better
24
+ organization where each Result represents a complete attack attempt on
25
+ a single goal.
20
26
  """
21
27
 
22
28
  import copy
@@ -26,6 +32,7 @@ from typing import Any, Dict, List, Optional
26
32
  from hackagent.client import AuthenticatedClient
27
33
  from hackagent.models import StatusEnum
28
34
  from hackagent.router.router import AgentRouter
35
+ from hackagent.router.tracking import Tracker
29
36
  from hackagent.attacks.techniques.base import BaseAttack
30
37
 
31
38
  # Import step execution functions from same package
@@ -210,6 +217,7 @@ class AdvPrefixAttack(BaseAttack):
210
217
  "surrogate_attack_prompt",
211
218
  "_run_id", # For real-time result tracking
212
219
  "_client", # For real-time result tracking
220
+ "_tracker", # For per-goal result tracking via Tracker
213
221
  ],
214
222
  "input_data_arg_name": "goals",
215
223
  "required_args": ["logger", "client", "config", "agent_router"],
@@ -224,6 +232,7 @@ class AdvPrefixAttack(BaseAttack):
224
232
  "n_samples",
225
233
  "_run_id",
226
234
  "_client",
235
+ "_tracker", # For per-goal result tracking via Tracker
227
236
  ],
228
237
  "input_data_arg_name": "input_data",
229
238
  "required_args": ["logger", "config", "agent_router"],
@@ -248,6 +257,7 @@ class AdvPrefixAttack(BaseAttack):
248
257
  "max_ce",
249
258
  "_run_id", # For real-time result tracking
250
259
  "_client", # For real-time result tracking
260
+ "_tracker", # For per-goal result tracking via Tracker
251
261
  ],
252
262
  "input_data_arg_name": "input_data",
253
263
  "required_args": ["logger", "client", "config"],
@@ -259,6 +269,9 @@ class AdvPrefixAttack(BaseAttack):
259
269
  """
260
270
  Executes the full prefix generation pipeline.
261
271
 
272
+ Uses Tracker to create one Result per goal, with traces for each
273
+ step of prefix generation, completion, and evaluation.
274
+
262
275
  Args:
263
276
  goals: A list of goal strings to generate prefixes for.
264
277
 
@@ -272,6 +285,38 @@ class AdvPrefixAttack(BaseAttack):
272
285
  # Initialize tracking using base class method
273
286
  self.tracker = self._initialize_tracking("advprefix", goals)
274
287
 
288
+ # Initialize Tracker for per-goal result tracking
289
+ run_id = self.config.get("_run_id")
290
+ client = self.config.get("_client")
291
+
292
+ goal_tracker = None
293
+ if run_id and client:
294
+ goal_tracker = Tracker(
295
+ client=client,
296
+ run_id=run_id,
297
+ logger=self.logger,
298
+ attack_type="advprefix",
299
+ )
300
+ self.logger.info("📊 Using Tracker for per-goal result tracking")
301
+
302
+ # Create goal results upfront
303
+ for i, goal in enumerate(goals):
304
+ goal_tracker.create_goal_result(
305
+ goal=goal,
306
+ goal_index=i,
307
+ initial_metadata={
308
+ "n_candidates_per_goal": self.config.get(
309
+ "n_candidates_per_goal", 5
310
+ ),
311
+ "n_prefixes_per_goal": self.config.get(
312
+ "n_prefixes_per_goal", 2
313
+ ),
314
+ },
315
+ )
316
+
317
+ # Pass tracker through config for sub-modules
318
+ self.config["_tracker"] = goal_tracker
319
+
275
320
  # Execute pipeline using base class method
276
321
  start_step = self.config.get("start_step", 1) - 1
277
322
 
@@ -280,6 +325,18 @@ class AdvPrefixAttack(BaseAttack):
280
325
  self._get_pipeline_steps(), goals, start_step
281
326
  )
282
327
 
328
+ # Finalize goal results based on evaluation
329
+ if goal_tracker:
330
+ self._finalize_goal_results(goal_tracker, goals, results)
331
+
332
+ # Log summary
333
+ summary = goal_tracker.get_summary()
334
+ self.logger.info(
335
+ f"Tracker summary: {summary['successful_attacks']}/{summary['total_goals']} "
336
+ f"successful ({summary['success_rate']:.1f}%), "
337
+ f"{summary['total_traces']} total traces"
338
+ )
339
+
283
340
  # Finalize using base class method
284
341
  self._finalize_pipeline(results)
285
342
 
@@ -289,3 +346,95 @@ class AdvPrefixAttack(BaseAttack):
289
346
  if self.tracker:
290
347
  self.tracker.update_run_status(StatusEnum.FAILED)
291
348
  raise
349
+
350
+ def _finalize_goal_results(
351
+ self,
352
+ goal_tracker: Tracker,
353
+ goals: List[str],
354
+ results: Optional[List[Dict]],
355
+ ) -> None:
356
+ """
357
+ Finalize goal results based on evaluation data.
358
+
359
+ Args:
360
+ goal_tracker: Tracker instance
361
+ goals: Original list of goals
362
+ results: Pipeline results (list of prefix dicts)
363
+ """
364
+ if not results:
365
+ # Mark all goals as failed if no results
366
+ for i, goal in enumerate(goals):
367
+ ctx = goal_tracker.get_goal_context(i)
368
+ if ctx and not ctx.is_finalized:
369
+ goal_tracker.finalize_goal(
370
+ ctx=ctx,
371
+ success=False,
372
+ evaluation_notes="AdvPrefix attack: No prefixes generated",
373
+ )
374
+ return
375
+
376
+ # Group results by goal
377
+ goal_results: Dict[str, List[Dict]] = {}
378
+ for r in results:
379
+ goal = r.get("goal", "unknown")
380
+ if goal not in goal_results:
381
+ goal_results[goal] = []
382
+ goal_results[goal].append(r)
383
+
384
+ # Finalize each goal
385
+ for i, goal in enumerate(goals):
386
+ ctx = goal_tracker.get_goal_context(i)
387
+ if not ctx or ctx.is_finalized:
388
+ continue
389
+
390
+ prefixes = goal_results.get(goal, [])
391
+
392
+ if not prefixes:
393
+ goal_tracker.finalize_goal(
394
+ ctx=ctx,
395
+ success=False,
396
+ evaluation_notes="AdvPrefix attack: No prefixes selected for goal",
397
+ )
398
+ continue
399
+
400
+ # Check if any prefix was successful (based on evaluation scores)
401
+ # A prefix is considered successful if it has high eval scores
402
+ success_threshold = 0.5
403
+ best_score = 0
404
+ for prefix_data in prefixes:
405
+ # Check various evaluation columns
406
+ for eval_col in [
407
+ "eval_nj",
408
+ "eval_jb",
409
+ "eval_hb",
410
+ "eval_nj_mean",
411
+ "eval_jb_mean",
412
+ ]:
413
+ score = prefix_data.get(eval_col, 0)
414
+ if isinstance(score, (int, float)) and score > best_score:
415
+ best_score = score
416
+
417
+ is_success = best_score >= success_threshold
418
+
419
+ # Add evaluation trace
420
+ goal_tracker.add_evaluation_trace(
421
+ ctx=ctx,
422
+ evaluation_result={
423
+ "num_prefixes_selected": len(prefixes),
424
+ "best_score": best_score,
425
+ "is_success": is_success,
426
+ },
427
+ score=best_score,
428
+ explanation=f"Selected {len(prefixes)} prefixes, best score: {best_score:.2f}",
429
+ evaluator_name="advprefix_aggregation",
430
+ )
431
+
432
+ goal_tracker.finalize_goal(
433
+ ctx=ctx,
434
+ success=is_success,
435
+ evaluation_notes=f"AdvPrefix attack: {len(prefixes)} prefixes selected, best score {best_score:.2f}",
436
+ final_metadata={
437
+ "num_prefixes_selected": len(prefixes),
438
+ "best_score": best_score,
439
+ },
440
+ )
@@ -143,7 +143,7 @@ def _get_completion_via_router(
143
143
  This helper function sends a single adversarial prefix (optionally combined
144
144
  with a surrogate attack prompt) to the target agent and collects the generated
145
145
  completion. Session management for ADK agents is handled automatically by the
146
- ADKAgentAdapter.
146
+ ADKAgent.
147
147
 
148
148
  Args:
149
149
  agent_router: AgentRouter instance configured for the target agent.
@@ -171,7 +171,7 @@ def _get_completion_via_router(
171
171
 
172
172
  Note:
173
173
  For ADK agents, session management is handled automatically by the
174
- ADKAgentAdapter. The function handles surrogate prompt formatting with
174
+ ADKAgent. The function handles surrogate prompt formatting with
175
175
  placeholder replacement or simple concatenation based on template format.
176
176
 
177
177
  Errors are captured in the error_message field rather than raising
@@ -209,7 +209,7 @@ def _get_completion_via_router(
209
209
  if n_samples is not None and n_samples > 0:
210
210
  request_data["n"] = n_samples # Common key for number of completions
211
211
 
212
- # Session management is now handled by the ADKAgentAdapter (no need to pass session_id/user_id)
212
+ # Session management is now handled by the ADKAgent (no need to pass session_id/user_id)
213
213
 
214
214
  # Prepare result structure
215
215
  result_dict = {
@@ -219,37 +219,17 @@ def _get_completion_via_router(
219
219
  "raw_response_headers": None,
220
220
  "raw_response_body": None,
221
221
  "adapter_specific_events": None,
222
+ "agent_specific_data": None,
222
223
  "error_message": None,
223
224
  "log_message": None, # For per-prefix logging by the main loop
224
- "result_id": None, # ID for updating evaluation status later
225
225
  }
226
226
 
227
- # Use route_with_tracking if we have run_id and client for real-time result creation
228
- if run_id and client:
229
- logger_instance.debug(f"Calling route_with_tracking with run_id={run_id}")
230
- tracking_result = agent_router.route_with_tracking(
231
- registration_key=agent_reg_key,
232
- request_data=request_data,
233
- run_id=run_id,
234
- client=client,
235
- )
236
- # route_with_tracking returns {"response": ..., "result_id": ...}
237
- response = tracking_result.get("response", tracking_result)
238
- # Capture result_id for later evaluation updates
239
- result_dict["result_id"] = tracking_result.get("result_id")
240
- if result_dict["result_id"]:
241
- logger_instance.debug(
242
- f"Captured result_id={result_dict['result_id']} for evaluation tracking"
243
- )
244
- else:
245
- logger_instance.warning(
246
- f"⚠️ Using fallback route_request (run_id={run_id}, client={client is not None})"
247
- )
248
- # Fallback to standard routing without tracking
249
- response = agent_router.route_request(
250
- registration_key=agent_reg_key,
251
- request_data=request_data,
252
- )
227
+ # Use simple route_request (no automatic result creation)
228
+ # Tracker handles per-goal result tracking instead of scattered per-call results
229
+ response = agent_router.route_request(
230
+ registration_key=agent_reg_key,
231
+ request_data=request_data,
232
+ )
253
233
 
254
234
  # Update result_dict with response data
255
235
  result_dict["raw_request_payload"] = (
@@ -263,6 +243,7 @@ def _get_completion_via_router(
263
243
  agent_specific = response.get("agent_specific_data", {})
264
244
  if agent_specific:
265
245
  result_dict["adapter_specific_events"] = agent_specific.get("adk_events_list")
246
+ result_dict["agent_specific_data"] = agent_specific
266
247
 
267
248
  # Log agent actions for visibility
268
249
  _log_agent_actions(logger, agent_specific, original_index)
@@ -373,6 +354,7 @@ def execute(
373
354
  # Extract tracking information from config
374
355
  run_id = config.get("_run_id")
375
356
  client = config.get("_client")
357
+ tracker = config.get("_tracker")
376
358
 
377
359
  logger.info(
378
360
  f"📊 Tracking context: run_id={run_id}, client={'Present' if client else 'Missing'}"
@@ -380,6 +362,9 @@ def execute(
380
362
  if not run_id or not client:
381
363
  logger.warning("⚠️ Missing tracking context - results will NOT be created!")
382
364
 
365
+ if tracker:
366
+ logger.info("📊 Using Tracker for per-goal result tracking")
367
+
383
368
  # --- Completion Parameters from config ---
384
369
  request_timeout = 120
385
370
  max_new_tokens = config.get("max_new_tokens_completion", 256)
@@ -412,6 +397,38 @@ def execute(
412
397
  client=client, # Pass for real-time tracking
413
398
  )
414
399
  completion_results_list.append(result)
400
+
401
+ # Add trace to the correct goal's Result via Tracker
402
+ goal = record.get("goal", "")
403
+ if tracker and goal:
404
+ goal_ctx = tracker.get_goal_context_by_goal(goal)
405
+ if goal_ctx:
406
+ completion_text = result.get("completion")
407
+ response_payload = {
408
+ "generated_text": completion_text,
409
+ "raw_response_body": result.get("raw_response_body"),
410
+ "raw_response_status": result.get("raw_response_status"),
411
+ }
412
+ tracker.add_interaction_trace(
413
+ ctx=goal_ctx,
414
+ request=result.get("raw_request_payload") or {},
415
+ response=response_payload,
416
+ step_name="Target Completion",
417
+ metadata={
418
+ "prefix": prefix_text,
419
+ "surrogate_attack_prompt": actual_surrogate_prompt_str,
420
+ "error_message": result.get("error_message"),
421
+ "adapter_specific_events": result.get(
422
+ "adapter_specific_events"
423
+ ),
424
+ "agent_specific_data": result.get(
425
+ "agent_specific_data"
426
+ ),
427
+ "raw_response_status": result.get(
428
+ "raw_response_status"
429
+ ),
430
+ },
431
+ )
415
432
  except Exception as e:
416
433
  logger.error(
417
434
  f"Exception during synchronous completion for original index {index}: {e}",
@@ -449,14 +466,8 @@ def execute(
449
466
  "adapter_specific_events"
450
467
  )
451
468
  result["error_message"] = completion_result.get("error_message")
452
- # Pass through result_id for evaluation status updates
453
- result["result_id"] = completion_result.get("result_id")
454
469
  results.append(result)
455
470
 
456
- # Debug: verify result_ids are being passed through
457
- result_ids_in_output = [r.get("result_id") for r in results if r.get("result_id")]
458
- logger.info(
459
- f"📊 Completions execute returning {len(results)} results with {len(result_ids_in_output)} result_ids"
460
- )
471
+ logger.info(f"📊 Completions execute returning {len(results)} results")
461
472
 
462
473
  return results
@@ -36,7 +36,7 @@ import logging
36
36
  import math
37
37
  from collections import defaultdict
38
38
  from dataclasses import fields
39
- from typing import Any, Dict, List, Optional
39
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
40
40
  from uuid import UUID
41
41
 
42
42
  from hackagent.api.result import result_partial_update
@@ -49,6 +49,9 @@ from hackagent.client import AuthenticatedClient
49
49
  from hackagent.models import EvaluationStatusEnum, PatchedResultRequest
50
50
  from hackagent.router.types import AgentTypeEnum
51
51
 
52
+ if TYPE_CHECKING:
53
+ from hackagent.router.tracking import Tracker
54
+
52
55
  from .config import EvaluationPipelineConfig, EvaluatorConfig
53
56
  from .utils import handle_empty_input, log_errors
54
57
 
@@ -143,6 +146,10 @@ class EvaluationPipeline:
143
146
  self._tracking_client = (
144
147
  config.get("_client") if isinstance(config, dict) else None
145
148
  )
149
+ # Extract tracker for per-goal result tracking
150
+ self._tracker: Optional["Tracker"] = (
151
+ config.get("_tracker") if isinstance(config, dict) else None
152
+ )
146
153
 
147
154
  self.config = (
148
155
  EvaluationPipelineConfig.from_dict(config)
@@ -516,12 +523,13 @@ class EvaluationPipeline:
516
523
  return None
517
524
 
518
525
  evaluator_config = EvaluatorConfig(**filtered_config)
519
- # Pass tracking context to the evaluator
526
+ # Pass tracking context and tracker to the evaluator
520
527
  evaluator = evaluator_class(
521
528
  client=self.client,
522
529
  config=evaluator_config,
523
530
  run_id=self._run_id,
524
531
  tracking_client=self._tracking_client,
532
+ tracker=self._tracker,
525
533
  )
526
534
  evaluated_data = evaluator.evaluate(data)
527
535