agent-learning-kit 0.1.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.
Files changed (642) hide show
  1. agent_learning_kit-0.1.0.dist-info/METADATA +381 -0
  2. agent_learning_kit-0.1.0.dist-info/RECORD +642 -0
  3. agent_learning_kit-0.1.0.dist-info/WHEEL +4 -0
  4. agent_learning_kit-0.1.0.dist-info/entry_points.txt +5 -0
  5. agent_learning_kit-0.1.0.dist-info/licenses/LICENSE +173 -0
  6. agent_learning_kit-0.1.0.dist-info/licenses/NOTICE +7 -0
  7. fi/__init__.py +5 -0
  8. fi/alk/__init__.py +57 -0
  9. fi/alk/_facade.py +31 -0
  10. fi/alk/_module_alias.py +68 -0
  11. fi/alk/_paths.py +14 -0
  12. fi/alk/_schema.py +522 -0
  13. fi/alk/actions.py +727 -0
  14. fi/alk/bench/__init__.py +517 -0
  15. fi/alk/bench/_codeexec.py +213 -0
  16. fi/alk/bench/_coding.py +215 -0
  17. fi/alk/bench/_docker.py +237 -0
  18. fi/alk/bench/_grader.py +286 -0
  19. fi/alk/bench/_pull.py +212 -0
  20. fi/alk/bench/_voice.py +147 -0
  21. fi/alk/capabilities.py +627 -0
  22. fi/alk/cli.py +6396 -0
  23. fi/alk/config.py +130 -0
  24. fi/alk/cua_loop.py +562 -0
  25. fi/alk/evals.py +2351 -0
  26. fi/alk/extensions.py +163 -0
  27. fi/alk/harness/ARCHITECTURE.md +231 -0
  28. fi/alk/harness/DESIGN.md +246 -0
  29. fi/alk/harness/ENVIRONMENT_CONFORMANCE.md +127 -0
  30. fi/alk/harness/HOW-IT-WORKS.md +297 -0
  31. fi/alk/harness/IMPLEMENTATION_AND_VALIDATION_STATUS.md +229 -0
  32. fi/alk/harness/README.md +417 -0
  33. fi/alk/harness/__init__.py +77 -0
  34. fi/alk/harness/__main__.py +3 -0
  35. fi/alk/harness/amend.py +312 -0
  36. fi/alk/harness/artifacts.py +319 -0
  37. fi/alk/harness/authoring_entrypoint.py +189 -0
  38. fi/alk/harness/authoring_runtime_validation.py +267 -0
  39. fi/alk/harness/backends/README.md +43 -0
  40. fi/alk/harness/backends/__init__.py +122 -0
  41. fi/alk/harness/backends/base.py +241 -0
  42. fi/alk/harness/backends/claude.py +211 -0
  43. fi/alk/harness/backends/files.py +182 -0
  44. fi/alk/harness/backends/vertex_gemini.py +457 -0
  45. fi/alk/harness/background_noise.py +95 -0
  46. fi/alk/harness/build.py +385 -0
  47. fi/alk/harness/bundle.py +593 -0
  48. fi/alk/harness/bundle_author_v2.py +1831 -0
  49. fi/alk/harness/bundle_v2.py +719 -0
  50. fi/alk/harness/call_runner.py +1440 -0
  51. fi/alk/harness/callback_http_adapter.py +111 -0
  52. fi/alk/harness/catalogue.py +287 -0
  53. fi/alk/harness/chat.py +428 -0
  54. fi/alk/harness/chat_call_runner.py +506 -0
  55. fi/alk/harness/checks.py +136 -0
  56. fi/alk/harness/cli.py +1354 -0
  57. fi/alk/harness/config.py +338 -0
  58. fi/alk/harness/contract.py +718 -0
  59. fi/alk/harness/credentials.py +674 -0
  60. fi/alk/harness/data/persona_vocabulary.json +111 -0
  61. fi/alk/harness/environment.py +99 -0
  62. fi/alk/harness/environment_plan.py +168 -0
  63. fi/alk/harness/events.py +125 -0
  64. fi/alk/harness/executor.py +304 -0
  65. fi/alk/harness/folder.py +234 -0
  66. fi/alk/harness/generated_runtime.py +815 -0
  67. fi/alk/harness/github.py +72 -0
  68. fi/alk/harness/hosted_authoring_entrypoint.py +183 -0
  69. fi/alk/harness/hosted_entrypoint.py +2402 -0
  70. fi/alk/harness/hosted_scheduler.py +2218 -0
  71. fi/alk/harness/job.py +426 -0
  72. fi/alk/harness/judge.py +184 -0
  73. fi/alk/harness/livekit_source.py +50 -0
  74. fi/alk/harness/livekit_tool_trace_bootstrap.py +71 -0
  75. fi/alk/harness/observability.py +208 -0
  76. fi/alk/harness/outbound.py +3252 -0
  77. fi/alk/harness/packaging.py +515 -0
  78. fi/alk/harness/persona_guides.py +157 -0
  79. fi/alk/harness/platform.py +692 -0
  80. fi/alk/harness/process_preflight.py +764 -0
  81. fi/alk/harness/process_runtime.py +5670 -0
  82. fi/alk/harness/prove.py +425 -0
  83. fi/alk/harness/provider_import.py +703 -0
  84. fi/alk/harness/provider_lifecycle.py +392 -0
  85. fi/alk/harness/provision.py +2896 -0
  86. fi/alk/harness/reception.py +147 -0
  87. fi/alk/harness/retell_chat_call_runner.py +373 -0
  88. fi/alk/harness/run/__init__.py +296 -0
  89. fi/alk/harness/run/alk.py +184 -0
  90. fi/alk/harness/run/call.py +162 -0
  91. fi/alk/harness/run/conversation.py +264 -0
  92. fi/alk/harness/run/data/voices_by_language_and_gender.json +693 -0
  93. fi/alk/harness/run/evidence.py +195 -0
  94. fi/alk/harness/run/grade.py +598 -0
  95. fi/alk/harness/run/live.py +297 -0
  96. fi/alk/harness/run/models.py +56 -0
  97. fi/alk/harness/run/platform_evals.py +227 -0
  98. fi/alk/harness/run/sdk_voice.py +130 -0
  99. fi/alk/harness/run/simulation.py +1209 -0
  100. fi/alk/harness/run/stage.py +91 -0
  101. fi/alk/harness/run/targets.py +508 -0
  102. fi/alk/harness/run/tools.py +601 -0
  103. fi/alk/harness/run/voice.py +340 -0
  104. fi/alk/harness/runtime.py +172 -0
  105. fi/alk/harness/sandbox_server.py +2011 -0
  106. fi/alk/harness/sandbox_worker.py +44 -0
  107. fi/alk/harness/scenario.py +1048 -0
  108. fi/alk/harness/scenario_source.py +879 -0
  109. fi/alk/harness/scenario_tools.py +1143 -0
  110. fi/alk/harness/scenarios.py +915 -0
  111. fi/alk/harness/secrets.py +168 -0
  112. fi/alk/harness/service_catalog.py +97 -0
  113. fi/alk/harness/session.py +391 -0
  114. fi/alk/harness/sessions.py +372 -0
  115. fi/alk/harness/simulator.py +76 -0
  116. fi/alk/harness/simulator_voice.py +928 -0
  117. fi/alk/harness/skills/build-environment/SKILL.md +538 -0
  118. fi/alk/harness/skills/harness.md +131 -0
  119. fi/alk/harness/skills/kinds/chat.md +48 -0
  120. fi/alk/harness/skills/kinds/voice-voicemail.md +63 -0
  121. fi/alk/harness/skills/kinds/voice.md +59 -0
  122. fi/alk/harness/skills/plan-suite/SKILL.md +103 -0
  123. fi/alk/harness/skills/provision-environment/SKILL.md +136 -0
  124. fi/alk/harness/skills/run-scenarios/SKILL.md +112 -0
  125. fi/alk/harness/skills/understand-agent/SKILL.md +251 -0
  126. fi/alk/harness/skills/write-scenarios/SKILL.md +606 -0
  127. fi/alk/harness/skills/write-scenarios/references/refusals.md +28 -0
  128. fi/alk/harness/skills/write-scenarios/references/world-api.md +92 -0
  129. fi/alk/harness/source_data_invariants.py +444 -0
  130. fi/alk/harness/source_tool_evidence.py +79 -0
  131. fi/alk/harness/sources.py +253 -0
  132. fi/alk/harness/spend.py +140 -0
  133. fi/alk/harness/tool_trace_proxy.py +104 -0
  134. fi/alk/harness/tools.py +1018 -0
  135. fi/alk/harness/understand.py +169 -0
  136. fi/alk/harness/voicemail_audio.py +74 -0
  137. fi/alk/harness/world/__init__.py +33 -0
  138. fi/alk/harness/world/errors.py +68 -0
  139. fi/alk/harness/world/expectations.py +91 -0
  140. fi/alk/harness/world/handle.py +538 -0
  141. fi/alk/harness/world/kinds.py +196 -0
  142. fi/alk/harness/world/mutate.py +186 -0
  143. fi/alk/harness/world/probe.py +413 -0
  144. fi/alk/harness/world/provision.py +511 -0
  145. fi/alk/harness/world/provisioned.py +191 -0
  146. fi/alk/harness/world/runtime.py +616 -0
  147. fi/alk/harness/world/snapshot.py +288 -0
  148. fi/alk/harness/world/stores/__init__.py +305 -0
  149. fi/alk/harness/world/stores/container.py +215 -0
  150. fi/alk/harness/world/stores/inprocess.py +346 -0
  151. fi/alk/harness/world/stores/postgres.py +481 -0
  152. fi/alk/harness/world/stores/prove.py +202 -0
  153. fi/alk/harness/world/stores/sqlite.py +245 -0
  154. fi/alk/harness/world/stores/written.py +182 -0
  155. fi/alk/harness/world/tools.py +1516 -0
  156. fi/alk/harness/world/workspace.py +144 -0
  157. fi/alk/image_loop.py +453 -0
  158. fi/alk/image_perturb.py +241 -0
  159. fi/alk/improve.py +274 -0
  160. fi/alk/live/__init__.py +154 -0
  161. fi/alk/live/_attribution.py +184 -0
  162. fi/alk/live/_capture.py +264 -0
  163. fi/alk/live/_codec.py +391 -0
  164. fi/alk/live/_contract.py +134 -0
  165. fi/alk/live/_loopback.py +316 -0
  166. fi/alk/live/_perturb.py +449 -0
  167. fi/alk/live/_runner.py +386 -0
  168. fi/alk/live/_stats.py +561 -0
  169. fi/alk/live/_transcript.py +240 -0
  170. fi/alk/live/_workers/__init__.py +9 -0
  171. fi/alk/live/_workers/a2a_worker.py +316 -0
  172. fi/alk/live/_workers/langgraph_worker.py +217 -0
  173. fi/alk/live/_workers/livekit_worker.py +207 -0
  174. fi/alk/live/_workers/mcp_loopback_server.py +46 -0
  175. fi/alk/live/_workers/mcp_worker.py +158 -0
  176. fi/alk/live/_workers/pipecat_worker.py +189 -0
  177. fi/alk/live/a2a_lane.py +138 -0
  178. fi/alk/live/langgraph_lane.py +339 -0
  179. fi/alk/live/livekit_lane.py +376 -0
  180. fi/alk/live/mcp_lane.py +172 -0
  181. fi/alk/live/pipecat_lane.py +341 -0
  182. fi/alk/live/voice_redteam.py +494 -0
  183. fi/alk/loss.py +306 -0
  184. fi/alk/optimize.py +36260 -0
  185. fi/alk/practice/__init__.py +51 -0
  186. fi/alk/practice/_assess.py +103 -0
  187. fi/alk/practice/_budget.py +81 -0
  188. fi/alk/practice/_calibrate.py +69 -0
  189. fi/alk/practice/_capstone.py +86 -0
  190. fi/alk/practice/_contract.py +91 -0
  191. fi/alk/practice/_diagnose.py +79 -0
  192. fi/alk/practice/_drill.py +196 -0
  193. fi/alk/practice/_experiment.py +720 -0
  194. fi/alk/practice/_schedule.py +102 -0
  195. fi/alk/practice/_store.py +194 -0
  196. fi/alk/practice/_trainer.py +245 -0
  197. fi/alk/practice/_update.py +125 -0
  198. fi/alk/redteam.py +2621 -0
  199. fi/alk/rewardhack.py +237 -0
  200. fi/alk/simulate.py +10351 -0
  201. fi/alk/studio/__init__.py +82 -0
  202. fi/alk/studio/_bias.py +314 -0
  203. fi/alk/studio/_calibration.py +522 -0
  204. fi/alk/studio/_coverage.py +262 -0
  205. fi/alk/studio/_download.py +665 -0
  206. fi/alk/studio/_fidelity_attack.py +114 -0
  207. fi/alk/studio/_generate.py +652 -0
  208. fi/alk/studio/_library.py +370 -0
  209. fi/alk/studio/_scan.py +134 -0
  210. fi/alk/studio/_upgrade.py +42 -0
  211. fi/alk/studio/_vendor.py +172 -0
  212. fi/alk/suite.py +4200 -0
  213. fi/alk/tasks.py +828 -0
  214. fi/alk/telemetry/__init__.py +149 -0
  215. fi/alk/telemetry/_contract.py +141 -0
  216. fi/alk/telemetry/_emit.py +182 -0
  217. fi/alk/telemetry/_ledger.py +296 -0
  218. fi/alk/telemetry/_queue.py +127 -0
  219. fi/alk/telemetry/_row.py +294 -0
  220. fi/alk/telemetry/_run.py +233 -0
  221. fi/alk/telemetry/_sync.py +193 -0
  222. fi/alk/telemetry/_url.py +119 -0
  223. fi/alk/trinity.py +49397 -0
  224. fi/alk/voice_loop.py +174 -0
  225. fi/api/__init__.py +1 -0
  226. fi/api/auth.py +137 -0
  227. fi/api/types.py +29 -0
  228. fi/cli/__init__.py +9 -0
  229. fi/cli/assertions/__init__.py +25 -0
  230. fi/cli/assertions/conditions.py +76 -0
  231. fi/cli/assertions/evaluator.py +286 -0
  232. fi/cli/assertions/exit_codes.py +20 -0
  233. fi/cli/assertions/parser.py +131 -0
  234. fi/cli/assertions/reporter.py +194 -0
  235. fi/cli/commands/__init__.py +9 -0
  236. fi/cli/commands/config.py +165 -0
  237. fi/cli/commands/export.py +208 -0
  238. fi/cli/commands/init.py +112 -0
  239. fi/cli/commands/list_cmd.py +213 -0
  240. fi/cli/commands/run.py +486 -0
  241. fi/cli/commands/validate.py +173 -0
  242. fi/cli/commands/view.py +424 -0
  243. fi/cli/config/__init__.py +6 -0
  244. fi/cli/config/defaults.py +206 -0
  245. fi/cli/config/loader.py +155 -0
  246. fi/cli/config/schema.py +174 -0
  247. fi/cli/main.py +78 -0
  248. fi/cli/output/__init__.py +6 -0
  249. fi/cli/output/formatters.py +106 -0
  250. fi/cli/output/reporters.py +46 -0
  251. fi/cli/storage/__init__.py +5 -0
  252. fi/cli/storage/run_history.py +249 -0
  253. fi/cli/utils/__init__.py +5 -0
  254. fi/cli/utils/console.py +44 -0
  255. fi/evals/__init__.py +131 -0
  256. fi/evals/autoeval/__init__.py +137 -0
  257. fi/evals/autoeval/analyzer.py +211 -0
  258. fi/evals/autoeval/config.py +244 -0
  259. fi/evals/autoeval/export.py +213 -0
  260. fi/evals/autoeval/interactive.py +283 -0
  261. fi/evals/autoeval/pipeline.py +625 -0
  262. fi/evals/autoeval/prompts.py +139 -0
  263. fi/evals/autoeval/recommender.py +242 -0
  264. fi/evals/autoeval/rules.py +589 -0
  265. fi/evals/autoeval/templates.py +299 -0
  266. fi/evals/autoeval/types.py +232 -0
  267. fi/evals/core/__init__.py +16 -0
  268. fi/evals/core/cloud_registry.py +184 -0
  269. fi/evals/core/engines.py +368 -0
  270. fi/evals/core/evaluate.py +319 -0
  271. fi/evals/core/judge_prompt.py +90 -0
  272. fi/evals/core/prompt_generator.py +83 -0
  273. fi/evals/core/registry.py +57 -0
  274. fi/evals/core/result.py +55 -0
  275. fi/evals/evaluator.py +721 -0
  276. fi/evals/execution.py +168 -0
  277. fi/evals/feedback/__init__.py +32 -0
  278. fi/evals/feedback/calibrator.py +160 -0
  279. fi/evals/feedback/collector.py +214 -0
  280. fi/evals/feedback/hooks.py +81 -0
  281. fi/evals/feedback/retriever.py +128 -0
  282. fi/evals/feedback/store.py +272 -0
  283. fi/evals/feedback/types.py +99 -0
  284. fi/evals/framework/README.md +79 -0
  285. fi/evals/framework/__init__.py +267 -0
  286. fi/evals/framework/backends/Dockerfile.eval-runner +33 -0
  287. fi/evals/framework/backends/__init__.py +99 -0
  288. fi/evals/framework/backends/_container.py +141 -0
  289. fi/evals/framework/backends/_utils.py +145 -0
  290. fi/evals/framework/backends/base.py +223 -0
  291. fi/evals/framework/backends/celery_backend.py +417 -0
  292. fi/evals/framework/backends/celery_worker.py +78 -0
  293. fi/evals/framework/backends/kubernetes_backend.py +665 -0
  294. fi/evals/framework/backends/ray_backend.py +521 -0
  295. fi/evals/framework/backends/temporal.py +350 -0
  296. fi/evals/framework/backends/temporal_worker.py +126 -0
  297. fi/evals/framework/backends/thread_pool.py +286 -0
  298. fi/evals/framework/context.py +258 -0
  299. fi/evals/framework/enrichment.py +306 -0
  300. fi/evals/framework/evals/__init__.py +68 -0
  301. fi/evals/framework/evals/agentic.py +399 -0
  302. fi/evals/framework/evals/builder.py +609 -0
  303. fi/evals/framework/evals/semantic.py +142 -0
  304. fi/evals/framework/evaluator.py +647 -0
  305. fi/evals/framework/evaluators/__init__.py +22 -0
  306. fi/evals/framework/evaluators/blocking.py +347 -0
  307. fi/evals/framework/evaluators/non_blocking.py +577 -0
  308. fi/evals/framework/propagation.py +421 -0
  309. fi/evals/framework/protocols.py +385 -0
  310. fi/evals/framework/registry.py +370 -0
  311. fi/evals/framework/resilience/__init__.py +150 -0
  312. fi/evals/framework/resilience/circuit_breaker.py +309 -0
  313. fi/evals/framework/resilience/degradation.py +355 -0
  314. fi/evals/framework/resilience/health.py +505 -0
  315. fi/evals/framework/resilience/rate_limiter.py +228 -0
  316. fi/evals/framework/resilience/retry.py +274 -0
  317. fi/evals/framework/resilience/types.py +288 -0
  318. fi/evals/framework/resilience/wrapper.py +433 -0
  319. fi/evals/framework/types.py +218 -0
  320. fi/evals/guardrails/README.md +915 -0
  321. fi/evals/guardrails/__init__.py +96 -0
  322. fi/evals/guardrails/backends/__init__.py +43 -0
  323. fi/evals/guardrails/backends/azure.py +361 -0
  324. fi/evals/guardrails/backends/base.py +88 -0
  325. fi/evals/guardrails/backends/generic_llm.py +163 -0
  326. fi/evals/guardrails/backends/granite.py +216 -0
  327. fi/evals/guardrails/backends/llamaguard.py +221 -0
  328. fi/evals/guardrails/backends/local_base.py +479 -0
  329. fi/evals/guardrails/backends/openai.py +365 -0
  330. fi/evals/guardrails/backends/qwen.py +170 -0
  331. fi/evals/guardrails/backends/shieldgemma.py +154 -0
  332. fi/evals/guardrails/backends/turing.py +235 -0
  333. fi/evals/guardrails/backends/vllm_client.py +321 -0
  334. fi/evals/guardrails/backends/wildguard.py +188 -0
  335. fi/evals/guardrails/base.py +888 -0
  336. fi/evals/guardrails/config.py +221 -0
  337. fi/evals/guardrails/discovery.py +243 -0
  338. fi/evals/guardrails/gateway.py +437 -0
  339. fi/evals/guardrails/registry.py +231 -0
  340. fi/evals/guardrails/scanners/__init__.py +127 -0
  341. fi/evals/guardrails/scanners/base.py +191 -0
  342. fi/evals/guardrails/scanners/code_injection.py +243 -0
  343. fi/evals/guardrails/scanners/eval_delegate.py +574 -0
  344. fi/evals/guardrails/scanners/invisible_chars.py +351 -0
  345. fi/evals/guardrails/scanners/jailbreak.py +412 -0
  346. fi/evals/guardrails/scanners/language.py +288 -0
  347. fi/evals/guardrails/scanners/pipeline.py +260 -0
  348. fi/evals/guardrails/scanners/regex.py +311 -0
  349. fi/evals/guardrails/scanners/secrets.py +274 -0
  350. fi/evals/guardrails/scanners/topics.py +649 -0
  351. fi/evals/guardrails/scanners/urls.py +341 -0
  352. fi/evals/guardrails/types.py +96 -0
  353. fi/evals/llm/__init__.py +3 -0
  354. fi/evals/llm/base_llm_provider.py +35 -0
  355. fi/evals/llm/providers/litellm.py +70 -0
  356. fi/evals/local/__init__.py +90 -0
  357. fi/evals/local/evaluator.py +690 -0
  358. fi/evals/local/execution_mode.py +121 -0
  359. fi/evals/local/llm.py +489 -0
  360. fi/evals/local/metrics/__init__.py +19 -0
  361. fi/evals/local/registry.py +360 -0
  362. fi/evals/manager.py +1018 -0
  363. fi/evals/manager_types.py +362 -0
  364. fi/evals/metrics/__init__.py +185 -0
  365. fi/evals/metrics/agents/__init__.py +74 -0
  366. fi/evals/metrics/agents/metrics.py +693 -0
  367. fi/evals/metrics/agents/report.py +36463 -0
  368. fi/evals/metrics/agents/types.py +160 -0
  369. fi/evals/metrics/base_llm_metric.py +111 -0
  370. fi/evals/metrics/base_metric.py +138 -0
  371. fi/evals/metrics/code_security/__init__.py +305 -0
  372. fi/evals/metrics/code_security/analyzer.py +985 -0
  373. fi/evals/metrics/code_security/benchmarks/__init__.py +73 -0
  374. fi/evals/metrics/code_security/benchmarks/builtin.py +750 -0
  375. fi/evals/metrics/code_security/benchmarks/loader.py +580 -0
  376. fi/evals/metrics/code_security/benchmarks/types.py +308 -0
  377. fi/evals/metrics/code_security/detectors/__init__.py +186 -0
  378. fi/evals/metrics/code_security/detectors/base.py +394 -0
  379. fi/evals/metrics/code_security/detectors/cryptography.py +345 -0
  380. fi/evals/metrics/code_security/detectors/injection.py +744 -0
  381. fi/evals/metrics/code_security/detectors/secrets.py +287 -0
  382. fi/evals/metrics/code_security/detectors/serialization.py +192 -0
  383. fi/evals/metrics/code_security/joint_metrics.py +588 -0
  384. fi/evals/metrics/code_security/judges/__init__.py +83 -0
  385. fi/evals/metrics/code_security/judges/base.py +238 -0
  386. fi/evals/metrics/code_security/judges/dual_judge.py +534 -0
  387. fi/evals/metrics/code_security/judges/llm_judge.py +301 -0
  388. fi/evals/metrics/code_security/judges/pattern_judge.py +515 -0
  389. fi/evals/metrics/code_security/metrics.py +388 -0
  390. fi/evals/metrics/code_security/modes/__init__.py +63 -0
  391. fi/evals/metrics/code_security/modes/adversarial.py +284 -0
  392. fi/evals/metrics/code_security/modes/autocomplete.py +198 -0
  393. fi/evals/metrics/code_security/modes/base.py +283 -0
  394. fi/evals/metrics/code_security/modes/instruct.py +253 -0
  395. fi/evals/metrics/code_security/modes/repair.py +230 -0
  396. fi/evals/metrics/code_security/reports/__init__.py +57 -0
  397. fi/evals/metrics/code_security/reports/generator.py +404 -0
  398. fi/evals/metrics/code_security/reports/leaderboard.py +509 -0
  399. fi/evals/metrics/code_security/types.py +534 -0
  400. fi/evals/metrics/function_calling/__init__.py +34 -0
  401. fi/evals/metrics/function_calling/metrics.py +573 -0
  402. fi/evals/metrics/function_calling/types.py +87 -0
  403. fi/evals/metrics/hallucination/__init__.py +54 -0
  404. fi/evals/metrics/hallucination/detector.py +149 -0
  405. fi/evals/metrics/hallucination/metrics.py +390 -0
  406. fi/evals/metrics/hallucination/nli.py +253 -0
  407. fi/evals/metrics/hallucination/sentinel.py +106 -0
  408. fi/evals/metrics/hallucination/types.py +132 -0
  409. fi/evals/metrics/heuristics/aggregation_metrics.py +85 -0
  410. fi/evals/metrics/heuristics/json_metrics.py +87 -0
  411. fi/evals/metrics/heuristics/similarity_metrics.py +375 -0
  412. fi/evals/metrics/heuristics/string_metrics.py +391 -0
  413. fi/evals/metrics/llm_as_judges/__init__.py +17 -0
  414. fi/evals/metrics/llm_as_judges/custom_judge/metric.py +112 -0
  415. fi/evals/metrics/llm_as_judges/custom_judge/prompts.py +26 -0
  416. fi/evals/metrics/llm_as_judges/types.py +48 -0
  417. fi/evals/metrics/rag/__init__.py +111 -0
  418. fi/evals/metrics/rag/advanced/__init__.py +14 -0
  419. fi/evals/metrics/rag/advanced/multi_hop.py +283 -0
  420. fi/evals/metrics/rag/advanced/source_attribution.py +344 -0
  421. fi/evals/metrics/rag/generation/__init__.py +17 -0
  422. fi/evals/metrics/rag/generation/answer_relevancy.py +176 -0
  423. fi/evals/metrics/rag/generation/context_utilization.py +245 -0
  424. fi/evals/metrics/rag/generation/faithfulness.py +241 -0
  425. fi/evals/metrics/rag/generation/groundedness.py +131 -0
  426. fi/evals/metrics/rag/rag_score.py +277 -0
  427. fi/evals/metrics/rag/retrieval/__init__.py +20 -0
  428. fi/evals/metrics/rag/retrieval/context_entity_recall.py +124 -0
  429. fi/evals/metrics/rag/retrieval/context_precision.py +158 -0
  430. fi/evals/metrics/rag/retrieval/context_recall.py +106 -0
  431. fi/evals/metrics/rag/retrieval/noise_sensitivity.py +163 -0
  432. fi/evals/metrics/rag/retrieval/ranking.py +261 -0
  433. fi/evals/metrics/rag/types.py +100 -0
  434. fi/evals/metrics/rag/utils/__init__.py +62 -0
  435. fi/evals/metrics/rag/utils/claims.py +189 -0
  436. fi/evals/metrics/rag/utils/entities.py +244 -0
  437. fi/evals/metrics/rag/utils/nli.py +92 -0
  438. fi/evals/metrics/rag/utils/similarity.py +345 -0
  439. fi/evals/metrics/structured/__init__.py +114 -0
  440. fi/evals/metrics/structured/field_completeness.py +313 -0
  441. fi/evals/metrics/structured/hierarchy_score.py +366 -0
  442. fi/evals/metrics/structured/json_validation.py +190 -0
  443. fi/evals/metrics/structured/schema_compliance.py +280 -0
  444. fi/evals/metrics/structured/structured_output_score.py +298 -0
  445. fi/evals/metrics/structured/types.py +108 -0
  446. fi/evals/metrics/structured/validators/__init__.py +30 -0
  447. fi/evals/metrics/structured/validators/base.py +189 -0
  448. fi/evals/metrics/structured/validators/json_validator.py +196 -0
  449. fi/evals/metrics/structured/validators/pydantic_validator.py +178 -0
  450. fi/evals/metrics/structured/validators/yaml_validator.py +248 -0
  451. fi/evals/otel/__init__.py +266 -0
  452. fi/evals/otel/config.py +400 -0
  453. fi/evals/otel/conventions.py +463 -0
  454. fi/evals/otel/enrichment.py +371 -0
  455. fi/evals/otel/instrumentors/__init__.py +140 -0
  456. fi/evals/otel/instrumentors/anthropic.py +517 -0
  457. fi/evals/otel/instrumentors/base.py +382 -0
  458. fi/evals/otel/instrumentors/openai.py +673 -0
  459. fi/evals/otel/processors/__init__.py +36 -0
  460. fi/evals/otel/processors/base.py +473 -0
  461. fi/evals/otel/processors/cost.py +445 -0
  462. fi/evals/otel/processors/evaluation.py +559 -0
  463. fi/evals/otel/processors/llm.py +462 -0
  464. fi/evals/otel/tracer.py +506 -0
  465. fi/evals/otel/types.py +232 -0
  466. fi/evals/otel_utils.py +23 -0
  467. fi/evals/protect.py +671 -0
  468. fi/evals/protect_input_adapter.py +154 -0
  469. fi/evals/streaming/__init__.py +88 -0
  470. fi/evals/streaming/buffer.py +213 -0
  471. fi/evals/streaming/evaluator.py +551 -0
  472. fi/evals/streaming/policy.py +307 -0
  473. fi/evals/streaming/scorers.py +368 -0
  474. fi/evals/streaming/types.py +238 -0
  475. fi/evals/templates.py +472 -0
  476. fi/evals/types.py +156 -0
  477. fi/opt/__init__.py +221 -0
  478. fi/opt/_objective_scoring.py +85 -0
  479. fi/opt/base/__init__.py +11 -0
  480. fi/opt/base/base_generator.py +33 -0
  481. fi/opt/base/base_mapper.py +26 -0
  482. fi/opt/base/base_optimizer.py +45 -0
  483. fi/opt/base/evaluator.py +211 -0
  484. fi/opt/components.py +3095 -0
  485. fi/opt/datamappers/__init__.py +3 -0
  486. fi/opt/datamappers/basic_mapper.py +40 -0
  487. fi/opt/deployment.py +1021 -0
  488. fi/opt/evidence.py +4332 -0
  489. fi/opt/generators/__init__.py +3 -0
  490. fi/opt/generators/litellm.py +66 -0
  491. fi/opt/integrations/__init__.py +23 -0
  492. fi/opt/integrations/generative_suite.py +410 -0
  493. fi/opt/integrations/simulate.py +1313 -0
  494. fi/opt/mutations.py +771 -0
  495. fi/opt/observability.py +4639 -0
  496. fi/opt/optimizer_trace.py +889 -0
  497. fi/opt/optimizers/__init__.py +80 -0
  498. fi/opt/optimizers/agent.py +331 -0
  499. fi/opt/optimizers/agent_bandit.py +392 -0
  500. fi/opt/optimizers/agent_curriculum.py +635 -0
  501. fi/opt/optimizers/agent_evolution.py +894 -0
  502. fi/opt/optimizers/agent_feedback.py +1863 -0
  503. fi/opt/optimizers/agent_pareto.py +547 -0
  504. fi/opt/optimizers/agent_social_memory.py +1113 -0
  505. fi/opt/optimizers/agent_tpe.py +321 -0
  506. fi/opt/optimizers/bayesian_search.py +449 -0
  507. fi/opt/optimizers/council.py +2075 -0
  508. fi/opt/optimizers/futureagi_replay.py +799 -0
  509. fi/opt/optimizers/gepa.py +322 -0
  510. fi/opt/optimizers/metaprompt.py +243 -0
  511. fi/opt/optimizers/promptwizard.py +417 -0
  512. fi/opt/optimizers/protegi.py +329 -0
  513. fi/opt/optimizers/random_search.py +224 -0
  514. fi/opt/research.py +518 -0
  515. fi/opt/simulation.py +260 -0
  516. fi/opt/targets.py +232 -0
  517. fi/opt/types.py +66 -0
  518. fi/opt/utils/__init__.py +4 -0
  519. fi/opt/utils/early_stopping.py +266 -0
  520. fi/opt/utils/setup_logging.py +82 -0
  521. fi/simulate/__init__.py +540 -0
  522. fi/simulate/_hashing.py +35 -0
  523. fi/simulate/_logging.py +10 -0
  524. fi/simulate/adapters.py +87 -0
  525. fi/simulate/agent/__init__.py +120 -0
  526. fi/simulate/agent/browser.py +658 -0
  527. fi/simulate/agent/definition.py +587 -0
  528. fi/simulate/agent/frameworks.py +3528 -0
  529. fi/simulate/agent/generic.py +8286 -0
  530. fi/simulate/agent/import_probe.py +227 -0
  531. fi/simulate/agent/memory.py +905 -0
  532. fi/simulate/agent/mocks.py +101 -0
  533. fi/simulate/agent/multi_agent.py +361 -0
  534. fi/simulate/agent/orchestration.py +903 -0
  535. fi/simulate/agent/realtime.py +665 -0
  536. fi/simulate/agent/wrapper.py +99 -0
  537. fi/simulate/agent/wrappers/__init__.py +18 -0
  538. fi/simulate/agent/wrappers/anthropic.py +62 -0
  539. fi/simulate/agent/wrappers/gemini.py +65 -0
  540. fi/simulate/agent/wrappers/http.py +404 -0
  541. fi/simulate/agent/wrappers/langchain.py +80 -0
  542. fi/simulate/agent/wrappers/openai.py +75 -0
  543. fi/simulate/agent/wrappers/websocket.py +326 -0
  544. fi/simulate/artifacts/__init__.py +11 -0
  545. fi/simulate/artifacts/manifest.py +62 -0
  546. fi/simulate/cli.py +20560 -0
  547. fi/simulate/endpoints/__init__.py +45 -0
  548. fi/simulate/endpoints/_http_actor.py +73 -0
  549. fi/simulate/endpoints/actor_sources.py +243 -0
  550. fi/simulate/endpoints/base.py +107 -0
  551. fi/simulate/endpoints/builtins.py +10 -0
  552. fi/simulate/endpoints/callable.py +95 -0
  553. fi/simulate/endpoints/http.py +76 -0
  554. fi/simulate/endpoints/livekit.py +138 -0
  555. fi/simulate/endpoints/originators.py +132 -0
  556. fi/simulate/endpoints/profiles.py +348 -0
  557. fi/simulate/endpoints/retell.py +633 -0
  558. fi/simulate/endpoints/vapi.py +205 -0
  559. fi/simulate/endpoints/websocket.py +76 -0
  560. fi/simulate/environment.py +33026 -0
  561. fi/simulate/environments/__init__.py +11 -0
  562. fi/simulate/environments/base.py +73 -0
  563. fi/simulate/environments/chat.py +697 -0
  564. fi/simulate/environments/voice.py +212 -0
  565. fi/simulate/evaluation/__init__.py +4 -0
  566. fi/simulate/evaluation/ai_eval.py +227 -0
  567. fi/simulate/evidence/__init__.py +35 -0
  568. fi/simulate/evidence/base.py +59 -0
  569. fi/simulate/evidence/caller_observed.py +50 -0
  570. fi/simulate/evidence/livekit_instrumentation.py +51 -0
  571. fi/simulate/evidence/livekit_room.py +50 -0
  572. fi/simulate/evidence/otel.py +49 -0
  573. fi/simulate/evidence/providers/__init__.py +24 -0
  574. fi/simulate/evidence/providers/base.py +61 -0
  575. fi/simulate/evidence/providers/retell.py +376 -0
  576. fi/simulate/evidence/providers/vapi.py +426 -0
  577. fi/simulate/hosted/__init__.py +32 -0
  578. fi/simulate/hosted/child_entrypoint.py +306 -0
  579. fi/simulate/hosted/job.py +150 -0
  580. fi/simulate/hosted/targets.py +53 -0
  581. fi/simulate/instrumentation/__init__.py +5 -0
  582. fi/simulate/instrumentation/livekit/__init__.py +122 -0
  583. fi/simulate/manifest.py +1033 -0
  584. fi/simulate/matrix_cli.py +165 -0
  585. fi/simulate/realtime/__init__.py +40 -0
  586. fi/simulate/realtime/events.py +107 -0
  587. fi/simulate/realtime/media.py +61 -0
  588. fi/simulate/realtime/session.py +91 -0
  589. fi/simulate/recording/__init__.py +5 -0
  590. fi/simulate/recording/room_recorder.py +326 -0
  591. fi/simulate/registry.py +185 -0
  592. fi/simulate/results/__init__.py +9 -0
  593. fi/simulate/results/base.py +18 -0
  594. fi/simulate/results/filesystem.py +71 -0
  595. fi/simulate/results/futureagi.py +1340 -0
  596. fi/simulate/runtime/__init__.py +85 -0
  597. fi/simulate/runtime/capabilities.py +40 -0
  598. fi/simulate/runtime/events.py +63 -0
  599. fi/simulate/runtime/failures.py +25 -0
  600. fi/simulate/runtime/ids.py +34 -0
  601. fi/simulate/runtime/plan.py +70 -0
  602. fi/simulate/runtime/planner.py +102 -0
  603. fi/simulate/runtime/report.py +174 -0
  604. fi/simulate/runtime/run.py +75 -0
  605. fi/simulate/runtime/runner.py +333 -0
  606. fi/simulate/runtime/spec.py +186 -0
  607. fi/simulate/simulation/__init__.py +30 -0
  608. fi/simulate/simulation/behavior_policy.py +425 -0
  609. fi/simulate/simulation/bridge/__init__.py +9 -0
  610. fi/simulate/simulation/bridge/audio.py +29 -0
  611. fi/simulate/simulation/bridge/connector.py +46 -0
  612. fi/simulate/simulation/bridge/livekit.py +252 -0
  613. fi/simulate/simulation/bridge/retell.py +188 -0
  614. fi/simulate/simulation/bridge/vapi.py +177 -0
  615. fi/simulate/simulation/contract.py +419 -0
  616. fi/simulate/simulation/engines/__init__.py +12 -0
  617. fi/simulate/simulation/engines/base.py +21 -0
  618. fi/simulate/simulation/engines/cloud.py +517 -0
  619. fi/simulate/simulation/engines/livekit.py +4167 -0
  620. fi/simulate/simulation/engines/local_text.py +89 -0
  621. fi/simulate/simulation/fidelity.py +374 -0
  622. fi/simulate/simulation/gemini_tts_stream.py +110 -0
  623. fi/simulate/simulation/generator.py +91 -0
  624. fi/simulate/simulation/goal_machine.py +185 -0
  625. fi/simulate/simulation/livekit_models.py +467 -0
  626. fi/simulate/simulation/matrix.py +170 -0
  627. fi/simulate/simulation/models.py +279 -0
  628. fi/simulate/simulation/runner.py +153 -0
  629. fi/simulate/simulation/synthetic.py +880 -0
  630. fi/simulate/simulation/voice_prompt.py +502 -0
  631. fi/simulate/simulator/__init__.py +55 -0
  632. fi/simulate/simulator/builtins.py +53 -0
  633. fi/simulate/suite.py +1288 -0
  634. fi/simulate/utils/routes.py +164 -0
  635. fi/simulate/voice.py +225 -0
  636. fi/simulate/voice_cli.py +182 -0
  637. fi/utils/__init__.py +1 -0
  638. fi/utils/constants.py +14 -0
  639. fi/utils/errors.py +200 -0
  640. fi/utils/executor.py +26 -0
  641. fi/utils/routes.py +119 -0
  642. fi/utils/utils.py +17 -0
@@ -0,0 +1,127 @@
1
+ """
2
+ Guardrails Scanners — fast, local content screening (<10ms per scanner).
3
+
4
+ No API keys, no GPU, no external services. Just regex + optional small ML models.
5
+
6
+ Usage:
7
+ from fi.evals.guardrails.scanners import (
8
+ ScannerPipeline,
9
+ JailbreakScanner,
10
+ CodeInjectionScanner,
11
+ SecretsScanner,
12
+ )
13
+
14
+ pipeline = ScannerPipeline([
15
+ JailbreakScanner(),
16
+ CodeInjectionScanner(),
17
+ SecretsScanner(),
18
+ ])
19
+
20
+ result = pipeline.scan("user input here")
21
+ if not result.passed:
22
+ print(f"Blocked by: {result.blocked_by}")
23
+
24
+ Available Scanners:
25
+ JailbreakScanner — prompt manipulation, DAN attacks, role-play exploits
26
+ CodeInjectionScanner — SQL, shell, path traversal, SSTI, LDAP, XXE
27
+ SecretsScanner — API keys, passwords, private keys, JWTs, DB URLs
28
+ MaliciousURLScanner — phishing, IP URLs, suspicious TLDs, shorteners
29
+ InvisibleCharScanner — zero-width chars, BIDI overrides, homoglyphs
30
+ LanguageScanner — language detection and filtering
31
+ TopicRestrictionScanner — keyword/embedding-based topic restriction
32
+ RegexScanner — custom regex patterns + common PII patterns
33
+ """
34
+
35
+ from fi.evals.guardrails.scanners.base import (
36
+ ScanResult,
37
+ ScannerAction,
38
+ )
39
+ from fi.evals.guardrails.scanners.pipeline import (
40
+ ScannerPipeline,
41
+ PipelineResult,
42
+ )
43
+
44
+ # Scanners
45
+ from fi.evals.guardrails.scanners.jailbreak import JailbreakScanner
46
+ from fi.evals.guardrails.scanners.code_injection import CodeInjectionScanner
47
+ from fi.evals.guardrails.scanners.secrets import SecretsScanner
48
+ from fi.evals.guardrails.scanners.urls import MaliciousURLScanner
49
+ from fi.evals.guardrails.scanners.invisible_chars import InvisibleCharScanner
50
+ from fi.evals.guardrails.scanners.language import LanguageScanner
51
+ from fi.evals.guardrails.scanners.topics import TopicRestrictionScanner
52
+ from fi.evals.guardrails.scanners.regex import RegexScanner, RegexPattern, COMMON_PATTERNS
53
+ from fi.evals.guardrails.scanners.eval_delegate import (
54
+ EvalDelegateScanner,
55
+ PIIScanner,
56
+ ToxicityScanner,
57
+ BiasScanner,
58
+ SafetyScanner,
59
+ ContentModerationScanner,
60
+ PromptInjectionScanner,
61
+ )
62
+
63
+
64
+ def create_default_pipeline(
65
+ jailbreak: bool = True,
66
+ code_injection: bool = True,
67
+ secrets: bool = True,
68
+ urls: bool = False,
69
+ invisible_chars: bool = False,
70
+ **kwargs,
71
+ ) -> ScannerPipeline:
72
+ """
73
+ Create a scanner pipeline with sensible defaults.
74
+
75
+ Args:
76
+ jailbreak: Enable jailbreak detection (default: True)
77
+ code_injection: Enable code injection detection (default: True)
78
+ secrets: Enable secrets detection (default: True)
79
+ urls: Enable malicious URL detection (default: False)
80
+ invisible_chars: Enable invisible character detection (default: False)
81
+
82
+ Returns:
83
+ Configured ScannerPipeline
84
+ """
85
+ scanners = []
86
+ if jailbreak:
87
+ scanners.append(JailbreakScanner(**kwargs.get("jailbreak_config", {})))
88
+ if code_injection:
89
+ scanners.append(CodeInjectionScanner(**kwargs.get("code_injection_config", {})))
90
+ if secrets:
91
+ scanners.append(SecretsScanner(**kwargs.get("secrets_config", {})))
92
+ if urls:
93
+ scanners.append(MaliciousURLScanner(**kwargs.get("urls_config", {})))
94
+ if invisible_chars:
95
+ scanners.append(InvisibleCharScanner(**kwargs.get("invisible_chars_config", {})))
96
+ return ScannerPipeline(scanners)
97
+
98
+
99
+ __all__ = [
100
+ # Result types
101
+ "ScanResult",
102
+ "ScannerAction",
103
+ "PipelineResult",
104
+ # Pipeline
105
+ "ScannerPipeline",
106
+ "create_default_pipeline",
107
+ # Scanners
108
+ "JailbreakScanner",
109
+ "CodeInjectionScanner",
110
+ "SecretsScanner",
111
+ "MaliciousURLScanner",
112
+ "InvisibleCharScanner",
113
+ "LanguageScanner",
114
+ "TopicRestrictionScanner",
115
+ "RegexScanner",
116
+ # Regex helpers
117
+ "RegexPattern",
118
+ "COMMON_PATTERNS",
119
+ # Eval-delegate scanners
120
+ "EvalDelegateScanner",
121
+ "PIIScanner",
122
+ "ToxicityScanner",
123
+ "BiasScanner",
124
+ "SafetyScanner",
125
+ "ContentModerationScanner",
126
+ "PromptInjectionScanner",
127
+ ]
@@ -0,0 +1,191 @@
1
+ """
2
+ Base Scanner Classes for Guardrails.
3
+
4
+ Provides the foundation for lightweight, fast content scanners
5
+ that run before model-based backends.
6
+ """
7
+
8
+ import time
9
+ from abc import ABC, abstractmethod
10
+ from dataclasses import dataclass, field
11
+ from typing import Any, Dict, List, Optional
12
+ from enum import Enum
13
+
14
+
15
+ class ScannerAction(Enum):
16
+ """Action to take when scanner detects a match."""
17
+ BLOCK = "block"
18
+ FLAG = "flag"
19
+ REDACT = "redact"
20
+ WARN = "warn"
21
+
22
+
23
+ @dataclass
24
+ class ScanMatch:
25
+ """A single match found by a scanner."""
26
+ pattern_name: str
27
+ matched_text: str
28
+ start: int
29
+ end: int
30
+ confidence: float = 1.0
31
+ metadata: Dict[str, Any] = field(default_factory=dict)
32
+
33
+
34
+ @dataclass
35
+ class ScanResult:
36
+ """Result from a scanner."""
37
+ passed: bool
38
+ scanner_name: str
39
+ category: str
40
+ matches: List[ScanMatch] = field(default_factory=list)
41
+ score: float = 0.0
42
+ action: ScannerAction = ScannerAction.BLOCK
43
+ reason: Optional[str] = None
44
+ latency_ms: float = 0.0
45
+ metadata: Dict[str, Any] = field(default_factory=dict)
46
+
47
+ @property
48
+ def has_matches(self) -> bool:
49
+ """Check if any matches were found."""
50
+ return len(self.matches) > 0
51
+
52
+ @property
53
+ def matched_patterns(self) -> List[str]:
54
+ """Get list of matched pattern names."""
55
+ return [m.pattern_name for m in self.matches]
56
+
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ """Convert to dictionary."""
59
+ return {
60
+ "passed": self.passed,
61
+ "scanner_name": self.scanner_name,
62
+ "category": self.category,
63
+ "matches": [
64
+ {
65
+ "pattern_name": m.pattern_name,
66
+ "matched_text": m.matched_text,
67
+ "start": m.start,
68
+ "end": m.end,
69
+ "confidence": m.confidence,
70
+ }
71
+ for m in self.matches
72
+ ],
73
+ "score": self.score,
74
+ "action": self.action.value,
75
+ "reason": self.reason,
76
+ "latency_ms": self.latency_ms,
77
+ }
78
+
79
+
80
+ class BaseScanner(ABC):
81
+ """
82
+ Base class for content scanners.
83
+
84
+ Scanners are lightweight, fast detectors that run before
85
+ model-based backends. They should complete in <10ms.
86
+
87
+ Usage:
88
+ class MyScanner(BaseScanner):
89
+ name = "my_scanner"
90
+ category = "custom"
91
+
92
+ def scan(self, content, context=None):
93
+ # Detection logic
94
+ return ScanResult(passed=True, ...)
95
+ """
96
+
97
+ name: str = "base_scanner"
98
+ category: str = "generic"
99
+ description: str = "Base scanner"
100
+ default_action: ScannerAction = ScannerAction.BLOCK
101
+
102
+ def __init__(self, action: Optional[ScannerAction] = None, enabled: bool = True):
103
+ """
104
+ Initialize scanner.
105
+
106
+ Args:
107
+ action: Action to take on match (default: BLOCK)
108
+ enabled: Whether scanner is enabled
109
+ """
110
+ self.action = action or self.default_action
111
+ self.enabled = enabled
112
+
113
+ @abstractmethod
114
+ def scan(self, content: str, context: Optional[str] = None) -> ScanResult:
115
+ """
116
+ Scan content for threats.
117
+
118
+ Args:
119
+ content: Content to scan
120
+ context: Optional context (e.g., user query for output scanning)
121
+
122
+ Returns:
123
+ ScanResult with detection results
124
+ """
125
+ pass
126
+
127
+ async def scan_async(self, content: str, context: Optional[str] = None) -> ScanResult:
128
+ """
129
+ Async version of scan. Defaults to sync implementation.
130
+
131
+ Override for truly async implementations.
132
+ """
133
+ return self.scan(content, context)
134
+
135
+ def _create_result(
136
+ self,
137
+ passed: bool,
138
+ matches: Optional[List[ScanMatch]] = None,
139
+ score: float = 0.0,
140
+ reason: Optional[str] = None,
141
+ latency_ms: float = 0.0,
142
+ metadata: Optional[Dict[str, Any]] = None,
143
+ ) -> ScanResult:
144
+ """Helper to create a ScanResult."""
145
+ return ScanResult(
146
+ passed=passed,
147
+ scanner_name=self.name,
148
+ category=self.category,
149
+ matches=matches or [],
150
+ score=score,
151
+ action=self.action if not passed else ScannerAction.WARN,
152
+ reason=reason,
153
+ latency_ms=latency_ms,
154
+ metadata=metadata or {},
155
+ )
156
+
157
+ def _timed_scan(self, func, content: str, context: Optional[str] = None) -> ScanResult:
158
+ """Helper to time a scan function."""
159
+ start = time.perf_counter()
160
+ result = func(content, context)
161
+ result.latency_ms = (time.perf_counter() - start) * 1000
162
+ return result
163
+
164
+
165
+ # Scanner registry for custom scanners
166
+ _SCANNER_REGISTRY: Dict[str, type] = {}
167
+
168
+
169
+ def register_scanner(name: str):
170
+ """
171
+ Decorator to register a custom scanner.
172
+
173
+ Usage:
174
+ @register_scanner("my_scanner")
175
+ class MyScanner(BaseScanner):
176
+ ...
177
+ """
178
+ def decorator(cls):
179
+ _SCANNER_REGISTRY[name] = cls
180
+ return cls
181
+ return decorator
182
+
183
+
184
+ def get_scanner(name: str) -> Optional[type]:
185
+ """Get a registered scanner class by name."""
186
+ return _SCANNER_REGISTRY.get(name)
187
+
188
+
189
+ def list_scanners() -> List[str]:
190
+ """List all registered scanner names."""
191
+ return list(_SCANNER_REGISTRY.keys())
@@ -0,0 +1,243 @@
1
+ """
2
+ Code Injection Scanner for Guardrails.
3
+
4
+ Detects SQL injection, shell injection, path traversal,
5
+ and other code injection attacks.
6
+ """
7
+
8
+ import re
9
+ import time
10
+ from typing import List, Optional, Tuple
11
+
12
+ from fi.evals.guardrails.scanners.base import (
13
+ BaseScanner,
14
+ ScanResult,
15
+ ScanMatch,
16
+ ScannerAction,
17
+ register_scanner,
18
+ )
19
+
20
+
21
+ # SQL Injection patterns
22
+ SQL_INJECTION_PATTERNS: List[Tuple[str, str, float]] = [
23
+ # Classic SQL injection
24
+ (r"(?i)'\s*(?:OR|AND)\s+['\d]+=\s*['\d]+", "sql_boolean_bypass", 0.95),
25
+ (r"(?i)'\s*;\s*(?:DROP|DELETE|TRUNCATE|ALTER|UPDATE|INSERT)\s+", "sql_destructive", 0.98),
26
+ (r"(?i)(?:UNION\s+(?:ALL\s+)?SELECT)", "sql_union_select", 0.95),
27
+ (r"(?i)(?:SELECT\s+.*\s+FROM\s+.*\s+WHERE)", "sql_select_where", 0.6),
28
+ (r"(?i)(?:INSERT\s+INTO\s+.*\s+VALUES)", "sql_insert", 0.7),
29
+ (r"(?i)(?:UPDATE\s+.*\s+SET\s+.*\s+WHERE)", "sql_update", 0.7),
30
+ (r"(?i)(?:DELETE\s+FROM\s+.*\s+WHERE)", "sql_delete", 0.8),
31
+ (r"(?i)(?:DROP\s+(?:TABLE|DATABASE|INDEX))", "sql_drop", 0.95),
32
+ (r"(?i)(?:--\s*$|#\s*$|/\*)", "sql_comment", 0.6),
33
+ (r"(?i)(?:EXEC(?:UTE)?\s*\(|xp_cmdshell)", "sql_exec", 0.95),
34
+ (r"(?i)(?:WAITFOR\s+DELAY|SLEEP\s*\()", "sql_time_based", 0.9),
35
+ (r"(?i)(?:BENCHMARK\s*\()", "sql_benchmark", 0.9),
36
+ (r"(?i)(?:LOAD_FILE|INTO\s+(?:OUT|DUMP)FILE)", "sql_file_access", 0.95),
37
+ (r"(?i)(?:information_schema|sys\.)", "sql_schema_access", 0.8),
38
+
39
+ # NoSQL Injection
40
+ (r'(?i)\$(?:where|gt|gte|lt|lte|ne|eq|in|nin|or|and|not|regex)', "nosql_operator", 0.85),
41
+ (r'(?i){\s*["\']?\$(?:where|gt|ne)', "nosql_query_injection", 0.9),
42
+ ]
43
+
44
+ # Shell Injection patterns
45
+ SHELL_INJECTION_PATTERNS: List[Tuple[str, str, float]] = [
46
+ # Command chaining
47
+ (r'(?:;|\||&&|\|\|)\s*(?:rm|cat|ls|wget|curl|nc|bash|sh|python|perl|ruby)\b', "shell_chain", 0.95),
48
+ (r'\$\([^)]+\)', "shell_command_substitution", 0.85),
49
+ (r'`[^`]+`', "shell_backtick", 0.85),
50
+
51
+ # Dangerous commands
52
+ (r'(?i)\brm\s+(?:-[rf]+\s+)?/', "shell_rm_root", 0.98),
53
+ (r'(?i)\b(?:wget|curl)\s+.*\s*\|\s*(?:bash|sh)', "shell_download_execute", 0.98),
54
+ (r'(?i)\b(?:nc|netcat|ncat)\s+-[elp]', "shell_netcat", 0.95),
55
+ (r'(?i)\b(?:bash|sh|zsh|ksh)\s+-[ci]', "shell_interactive", 0.85),
56
+ (r'(?i)\bchmod\s+[+0-7]*[xst]', "shell_chmod_exec", 0.7),
57
+ (r'(?i)\bchown\s+', "shell_chown", 0.6),
58
+
59
+ # Environment manipulation
60
+ (r'(?i)\bexport\s+\w+=', "shell_export", 0.5),
61
+ (r'(?i)\benv\s+-i', "shell_env_clear", 0.7),
62
+
63
+ # Reverse shells
64
+ (r'(?i)/dev/tcp/', "shell_dev_tcp", 0.95),
65
+ (r'(?i)mkfifo|mknod', "shell_fifo", 0.85),
66
+ ]
67
+
68
+ # Path Traversal patterns
69
+ PATH_TRAVERSAL_PATTERNS: List[Tuple[str, str, float]] = [
70
+ (r'(?:\.\.[\\/]){2,}', "path_traversal", 0.9),
71
+ (r'(?:\.\./|\.\.\\){3,}', "deep_path_traversal", 0.95),
72
+ (r'(?i)(?:\.\./|\.\.\\)(?:etc|var|usr|home|windows|system32)', "path_traversal_sensitive", 0.95),
73
+ (r'(?i)/etc/(?:passwd|shadow|hosts)', "unix_sensitive_file", 0.95),
74
+ (r'(?i)(?:C:\\|\\\\)(?:windows|system32|boot)', "windows_sensitive", 0.9),
75
+ (r'%(?:2e|2f|5c){2,}', "encoded_path_traversal", 0.9),
76
+ (r'(?i)file:///', "file_protocol", 0.7),
77
+ ]
78
+
79
+ # Template Injection patterns
80
+ TEMPLATE_INJECTION_PATTERNS: List[Tuple[str, str, float]] = [
81
+ (r'\{\{.*\}\}', "jinja_template", 0.7),
82
+ (r'\$\{[^}]+\}', "variable_interpolation", 0.7),
83
+ (r'<%.*%>', "erb_template", 0.8),
84
+ (r'#\{[^}]+\}', "ruby_interpolation", 0.7),
85
+ (r'\{\%.*\%\}', "jinja_statement", 0.75),
86
+ (r'(?i)\{\{.*(?:__class__|__mro__|__subclasses__|__globals__).*\}\}', "ssti_python", 0.95),
87
+ (r'(?i)\$\{.*(?:Runtime|ProcessBuilder|getClass).*\}', "ssti_java", 0.95),
88
+ ]
89
+
90
+ # LDAP Injection patterns
91
+ LDAP_INJECTION_PATTERNS: List[Tuple[str, str, float]] = [
92
+ (r'\)\s*\(\|', "ldap_or_injection", 0.85),
93
+ (r'\)\s*\(&', "ldap_and_injection", 0.85),
94
+ (r'\*\)\s*\(', "ldap_wildcard", 0.7),
95
+ ]
96
+
97
+ # XML/XXE Injection patterns
98
+ XML_INJECTION_PATTERNS: List[Tuple[str, str, float]] = [
99
+ (r'<!ENTITY\s+', "xxe_entity", 0.9),
100
+ (r'<!DOCTYPE\s+[^>]*\[', "xxe_doctype", 0.85),
101
+ (r'(?i)SYSTEM\s+["\'](?:file|http|ftp)://', "xxe_external", 0.95),
102
+ ]
103
+
104
+
105
+ @register_scanner("code_injection")
106
+ class CodeInjectionScanner(BaseScanner):
107
+ """
108
+ Scanner for detecting code injection attacks.
109
+
110
+ Detects:
111
+ - SQL injection (classic, blind, NoSQL)
112
+ - Shell/Command injection
113
+ - Path traversal attacks
114
+ - Template injection (SSTI)
115
+ - LDAP injection
116
+ - XML/XXE injection
117
+
118
+ Usage:
119
+ scanner = CodeInjectionScanner()
120
+ result = scanner.scan("'; DROP TABLE users; --")
121
+ if not result.passed:
122
+ print(f"Injection detected: {result.matched_patterns}")
123
+ """
124
+
125
+ name = "code_injection"
126
+ category = "code_injection"
127
+ description = "Detects SQL, shell, and other code injection attacks"
128
+ default_action = ScannerAction.BLOCK
129
+
130
+ def __init__(
131
+ self,
132
+ action: Optional[ScannerAction] = None,
133
+ enabled: bool = True,
134
+ threshold: float = 0.7,
135
+ check_sql: bool = True,
136
+ check_shell: bool = True,
137
+ check_path: bool = True,
138
+ check_template: bool = True,
139
+ check_ldap: bool = True,
140
+ check_xml: bool = True,
141
+ ):
142
+ """
143
+ Initialize code injection scanner.
144
+
145
+ Args:
146
+ action: Action on detection
147
+ enabled: Whether scanner is enabled
148
+ threshold: Minimum confidence to trigger
149
+ check_sql: Check for SQL injection
150
+ check_shell: Check for shell injection
151
+ check_path: Check for path traversal
152
+ check_template: Check for template injection
153
+ check_ldap: Check for LDAP injection
154
+ check_xml: Check for XML/XXE injection
155
+ """
156
+ super().__init__(action, enabled)
157
+ self.threshold = threshold
158
+
159
+ # Build pattern list based on enabled checks
160
+ patterns = []
161
+ if check_sql:
162
+ patterns.extend(SQL_INJECTION_PATTERNS)
163
+ if check_shell:
164
+ patterns.extend(SHELL_INJECTION_PATTERNS)
165
+ if check_path:
166
+ patterns.extend(PATH_TRAVERSAL_PATTERNS)
167
+ if check_template:
168
+ patterns.extend(TEMPLATE_INJECTION_PATTERNS)
169
+ if check_ldap:
170
+ patterns.extend(LDAP_INJECTION_PATTERNS)
171
+ if check_xml:
172
+ patterns.extend(XML_INJECTION_PATTERNS)
173
+
174
+ # Compile patterns
175
+ self._compiled_patterns = [
176
+ (re.compile(pattern), name, confidence)
177
+ for pattern, name, confidence in patterns
178
+ ]
179
+
180
+ def scan(self, content: str, context: Optional[str] = None) -> ScanResult:
181
+ """
182
+ Scan content for code injection attacks.
183
+
184
+ Args:
185
+ content: Content to scan
186
+ context: Optional context
187
+
188
+ Returns:
189
+ ScanResult with detection details
190
+ """
191
+ start = time.perf_counter()
192
+ matches = []
193
+ max_confidence = 0.0
194
+
195
+ for pattern, name, confidence in self._compiled_patterns:
196
+ for match in pattern.finditer(content):
197
+ matches.append(ScanMatch(
198
+ pattern_name=name,
199
+ matched_text=match.group(),
200
+ start=match.start(),
201
+ end=match.end(),
202
+ confidence=confidence,
203
+ ))
204
+ max_confidence = max(max_confidence, confidence)
205
+
206
+ latency = (time.perf_counter() - start) * 1000
207
+
208
+ # Filter by threshold
209
+ significant_matches = [m for m in matches if m.confidence >= self.threshold]
210
+
211
+ if significant_matches:
212
+ # Categorize the type of injection
213
+ categories = set()
214
+ for m in significant_matches:
215
+ if m.pattern_name.startswith("sql_") or m.pattern_name.startswith("nosql_"):
216
+ categories.add("SQL injection")
217
+ elif m.pattern_name.startswith("shell_"):
218
+ categories.add("Shell injection")
219
+ elif m.pattern_name.startswith("path_") or m.pattern_name.endswith("_sensitive"):
220
+ categories.add("Path traversal")
221
+ elif m.pattern_name.endswith("_template") or m.pattern_name.startswith("ssti_"):
222
+ categories.add("Template injection")
223
+ elif m.pattern_name.startswith("ldap_"):
224
+ categories.add("LDAP injection")
225
+ elif m.pattern_name.startswith("xxe_"):
226
+ categories.add("XXE injection")
227
+
228
+ return self._create_result(
229
+ passed=False,
230
+ matches=significant_matches,
231
+ score=max_confidence,
232
+ reason=f"Code injection detected: {', '.join(categories)}",
233
+ latency_ms=latency,
234
+ metadata={"injection_types": list(categories)},
235
+ )
236
+
237
+ return self._create_result(
238
+ passed=True,
239
+ matches=[],
240
+ score=0.0,
241
+ reason="No code injection patterns detected",
242
+ latency_ms=latency,
243
+ )