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,1033 @@
1
+ from __future__ import annotations
2
+
3
+ import copy
4
+ import importlib
5
+ import json
6
+ import os
7
+ import time
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Literal, Mapping, Optional, Sequence
11
+
12
+
13
+ CLI_SCHEMA_VERSION = "agent-simulate.cli.v1"
14
+ MANIFEST_SCHEMA_VERSION = CLI_SCHEMA_VERSION
15
+
16
+
17
+ class ManifestError(ValueError):
18
+ """Raised when a simulation manifest cannot be executed safely."""
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class ManifestRunOptions:
23
+ name: Optional[str] = None
24
+ threshold: Optional[float] = None
25
+ no_eval: bool = False
26
+ dry_run: bool = False
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class ManifestOptimizationOptions:
31
+ name: Optional[str] = None
32
+ threshold: Optional[float] = None
33
+ max_candidates: Optional[int] = None
34
+ dry_run: bool = False
35
+
36
+
37
+ def load_manifest_file(path: str | Path) -> Dict[str, Any]:
38
+ """Load a JSON/YAML manifest using the same validation as the CLI."""
39
+
40
+ return _cli().load_manifest(Path(path).expanduser().resolve())
41
+
42
+
43
+ load_manifest = load_manifest_file
44
+
45
+
46
+ def write_manifest_file(manifest: Mapping[str, Any], path: str | Path) -> Path:
47
+ """Write a portable simulation manifest as formatted JSON."""
48
+
49
+ destination = Path(path).expanduser().resolve()
50
+ destination.parent.mkdir(parents=True, exist_ok=True)
51
+ destination.write_text(
52
+ json.dumps(dict(manifest), indent=2, sort_keys=True, default=str) + "\n",
53
+ encoding="utf-8",
54
+ )
55
+ return destination
56
+
57
+
58
+ def public_result(result: Mapping[str, Any]) -> Dict[str, Any]:
59
+ """Return a JSON-safe result payload without local output bookkeeping."""
60
+
61
+ return _cli()._public_result(result)
62
+
63
+
64
+ def detect_manifest_command(
65
+ manifest: Mapping[str, Any],
66
+ ) -> Literal["run", "redteam", "optimize"]:
67
+ """Return the default command for a manifest shape."""
68
+
69
+ if manifest.get("optimization") is not None:
70
+ return "optimize"
71
+ if _has_redteam_block(manifest):
72
+ return "redteam"
73
+ return "run"
74
+
75
+
76
+ def required_manifest_env(manifest: Mapping[str, Any]) -> list[str]:
77
+ """Return all environment keys required before a manifest can execute."""
78
+
79
+ return sorted(_cli()._required_env(manifest))
80
+
81
+
82
+ def missing_manifest_env(manifest: Mapping[str, Any]) -> list[str]:
83
+ """Return required environment keys that are not present in os.environ."""
84
+
85
+ return [key for key in required_manifest_env(manifest) if not os.environ.get(key)]
86
+
87
+
88
+ def validate_manifest_env(manifest: Mapping[str, Any]) -> None:
89
+ """Raise ManifestError if required environment variables are missing."""
90
+
91
+ missing_env = missing_manifest_env(manifest)
92
+ if missing_env:
93
+ raise ManifestError(
94
+ "missing required environment variable(s): "
95
+ f"{', '.join(sorted(missing_env))}"
96
+ )
97
+
98
+
99
+ def apply_manifest_env(manifest: Mapping[str, Any]) -> None:
100
+ """Apply manifest env.set values to process env, matching CLI behavior."""
101
+
102
+ _cli()._apply_manifest_env(manifest)
103
+
104
+
105
+ def build_manifest_agent_callback(
106
+ agent: Mapping[str, Any],
107
+ *,
108
+ base_dir: str | Path = ".",
109
+ ) -> Any:
110
+ """Build the runtime callback for a manifest ``agent`` block.
111
+
112
+ Supports scripted, echo, python callable, and framework adapter agents using
113
+ the same loader/wrapper path as the CLI.
114
+ """
115
+
116
+ return _cli()._build_agent_callback(
117
+ copy.deepcopy(dict(agent)),
118
+ Path(base_dir).expanduser().resolve(),
119
+ )
120
+
121
+
122
+ def supported_manifest_environment_types() -> list[str]:
123
+ """Return the manifest environment types supported by the CLI/SDK registry."""
124
+
125
+ return sorted(_cli().MANIFEST_ENVIRONMENT_TYPES)
126
+
127
+
128
+ def build_manifest_environments(
129
+ environments: Any,
130
+ *,
131
+ base_dir: str | Path = ".",
132
+ ) -> list[Any]:
133
+ """Build environment adapters from manifest ``simulation.environments`` data."""
134
+
135
+ if environments in (None, ""):
136
+ specs: list[Any] = []
137
+ elif isinstance(environments, Mapping):
138
+ specs = [copy.deepcopy(dict(environments))]
139
+ else:
140
+ specs = copy.deepcopy(list(environments))
141
+ return _cli()._build_environments(specs, Path(base_dir).expanduser().resolve())
142
+
143
+
144
+ def prepare_redteam_manifest(manifest: Mapping[str, Any]) -> Dict[str, Any]:
145
+ """Return a red-team manifest with CLI defaults and generated environments applied."""
146
+
147
+ runtime_manifest = copy.deepcopy(dict(manifest))
148
+ _cli()._prepare_redteam_manifest(runtime_manifest)
149
+ return runtime_manifest
150
+
151
+
152
+ def _has_redteam_block(manifest: Mapping[str, Any]) -> bool:
153
+ return manifest.get("redteam") not in (None, "", [], {}) or manifest.get(
154
+ "red_team"
155
+ ) not in (None, "", [], {})
156
+
157
+
158
+ def _prepare_redteam_if_present(
159
+ manifest: Dict[str, Any],
160
+ cli: Any,
161
+ ) -> Optional[Dict[str, Any]]:
162
+ if not _has_redteam_block(manifest):
163
+ return None
164
+ return cli._prepare_redteam_manifest(manifest)
165
+
166
+
167
+ def _redteam_optimization_summary(
168
+ manifest: Mapping[str, Any],
169
+ cli: Any,
170
+ ) -> Optional[Dict[str, Any]]:
171
+ if not _has_redteam_block(manifest):
172
+ return None
173
+ runtime_manifest = copy.deepcopy(dict(manifest))
174
+ try:
175
+ return _prepare_redteam_if_present(runtime_manifest, cli)
176
+ except ManifestError as exc:
177
+ if "requires at least one adversarial_attack_pack" in str(exc):
178
+ return None
179
+ raise
180
+
181
+
182
+ async def run_local_text_manifest(
183
+ manifest: Mapping[str, Any],
184
+ manifest_path: str | Path,
185
+ ) -> Any:
186
+ """Run a local_text/local manifest and return the raw TestReport."""
187
+
188
+ return await _cli()._run_local_text_manifest(
189
+ manifest,
190
+ Path(manifest_path).expanduser().resolve(),
191
+ )
192
+
193
+
194
+ def evaluate_manifest_report(manifest: Mapping[str, Any], report: Any) -> Any:
195
+ """Score a report with the manifest's evaluation.agent_report block."""
196
+
197
+ return _cli()._evaluate_manifest_report(manifest, report)
198
+
199
+
200
+ def render_junit(result: Mapping[str, Any]) -> str:
201
+ """Render a CLI-compatible JUnit XML string for any manifest result."""
202
+
203
+ return _cli()._junit_xml(result)
204
+
205
+
206
+ def render_sarif(
207
+ result: Mapping[str, Any],
208
+ *,
209
+ manifest_path: str | Path = ".",
210
+ ) -> str:
211
+ """Render a SARIF 2.1.0 JSON string for result findings."""
212
+
213
+ return _cli()._sarif_json(result, Path(manifest_path).expanduser().resolve())
214
+
215
+
216
+ def render_markdown(
217
+ result: Mapping[str, Any],
218
+ *,
219
+ source_path: str | Path = ".",
220
+ ) -> str:
221
+ """Render the same Markdown report text produced by the CLI."""
222
+
223
+ return _cli()._markdown_text(result, Path(source_path).expanduser().resolve())
224
+
225
+
226
+ def create_baseline_file(
227
+ path: str | Path,
228
+ *,
229
+ name: Optional[str] = None,
230
+ ) -> Dict[str, Any]:
231
+ """Load a result artifact and return a compact compare-safe baseline."""
232
+
233
+ source_path = Path(path).expanduser().resolve()
234
+ return create_baseline(
235
+ load_manifest_file(source_path),
236
+ source_path=source_path,
237
+ name=name,
238
+ )
239
+
240
+
241
+ def create_baseline(
242
+ source: Mapping[str, Any],
243
+ *,
244
+ source_path: str | Path = ".",
245
+ name: Optional[str] = None,
246
+ ) -> Dict[str, Any]:
247
+ """Return a compact compare-safe baseline for a run/redteam/optimize result."""
248
+
249
+ started = time.time()
250
+ return _cli()._baseline_result(
251
+ source=copy.deepcopy(dict(source)),
252
+ source_path=Path(source_path).expanduser().resolve(),
253
+ name=name,
254
+ duration_seconds=round(time.time() - started, 4),
255
+ )
256
+
257
+
258
+ def compare_result_files(
259
+ baseline_path: str | Path,
260
+ current_path: str | Path,
261
+ *,
262
+ min_score_delta: float = 0.0,
263
+ max_new_findings: int = 0,
264
+ max_new_error_findings: int = 0,
265
+ min_metric_delta: Optional[float] = None,
266
+ name: Optional[str] = None,
267
+ ) -> Dict[str, Any]:
268
+ """Load two result artifacts and return the same compare payload as the CLI."""
269
+
270
+ baseline_path = Path(baseline_path).expanduser().resolve()
271
+ current_path = Path(current_path).expanduser().resolve()
272
+ return compare_results(
273
+ load_manifest_file(baseline_path),
274
+ load_manifest_file(current_path),
275
+ baseline_path=baseline_path,
276
+ current_path=current_path,
277
+ min_score_delta=min_score_delta,
278
+ max_new_findings=max_new_findings,
279
+ max_new_error_findings=max_new_error_findings,
280
+ min_metric_delta=min_metric_delta,
281
+ name=name,
282
+ )
283
+
284
+
285
+ def compare_results(
286
+ baseline: Mapping[str, Any],
287
+ current: Mapping[str, Any],
288
+ *,
289
+ baseline_path: str | Path = "baseline.json",
290
+ current_path: str | Path = "current.json",
291
+ min_score_delta: float = 0.0,
292
+ max_new_findings: int = 0,
293
+ max_new_error_findings: int = 0,
294
+ min_metric_delta: Optional[float] = None,
295
+ name: Optional[str] = None,
296
+ ) -> Dict[str, Any]:
297
+ """Compare baseline/current result payloads with stable gate semantics."""
298
+
299
+ started = time.time()
300
+ return _cli()._compare_results(
301
+ baseline=copy.deepcopy(dict(baseline)),
302
+ current=copy.deepcopy(dict(current)),
303
+ baseline_path=Path(baseline_path).expanduser().resolve(),
304
+ current_path=Path(current_path).expanduser().resolve(),
305
+ min_score_delta=float(min_score_delta),
306
+ max_new_findings=int(max_new_findings),
307
+ max_new_error_findings=int(max_new_error_findings),
308
+ min_metric_delta=min_metric_delta,
309
+ name=name,
310
+ duration_seconds=round(time.time() - started, 4),
311
+ )
312
+
313
+
314
+ def render_report_file(
315
+ path: str | Path,
316
+ *,
317
+ name: Optional[str] = None,
318
+ ) -> Dict[str, Any]:
319
+ """Load a result artifact and return a Markdown report payload."""
320
+
321
+ source_path = Path(path).expanduser().resolve()
322
+ return render_report(
323
+ load_manifest_file(source_path),
324
+ source_path=source_path,
325
+ name=name,
326
+ )
327
+
328
+
329
+ def render_report(
330
+ source: Mapping[str, Any],
331
+ *,
332
+ source_path: str | Path = ".",
333
+ name: Optional[str] = None,
334
+ ) -> Dict[str, Any]:
335
+ """Return a Markdown report payload for any manifest result."""
336
+
337
+ started = time.time()
338
+ return _cli()._report_result(
339
+ source=copy.deepcopy(dict(source)),
340
+ source_path=Path(source_path).expanduser().resolve(),
341
+ name=name,
342
+ duration_seconds=round(time.time() - started, 4),
343
+ )
344
+
345
+
346
+ def promote_to_regression_file(
347
+ path: str | Path,
348
+ *,
349
+ name: Optional[str] = None,
350
+ min_level: Literal["note", "warning", "error"] = "warning",
351
+ max_findings: int = 25,
352
+ required_env: Sequence[str] = (),
353
+ ) -> Dict[str, Any]:
354
+ """Load a result artifact and return a runnable red-team regression manifest."""
355
+
356
+ source_path = Path(path).expanduser().resolve()
357
+ return promote_to_regression(
358
+ load_manifest_file(source_path),
359
+ source_path=source_path,
360
+ name=name,
361
+ min_level=min_level,
362
+ max_findings=max_findings,
363
+ required_env=required_env,
364
+ )
365
+
366
+
367
+ def promote_to_regression(
368
+ source: Mapping[str, Any],
369
+ *,
370
+ source_path: str | Path = ".",
371
+ name: Optional[str] = None,
372
+ min_level: Literal["note", "warning", "error"] = "warning",
373
+ max_findings: int = 25,
374
+ required_env: Sequence[str] = (),
375
+ ) -> Dict[str, Any]:
376
+ """Return a regression-promotion payload with an embedded runnable manifest."""
377
+
378
+ started = time.time()
379
+ return _cli()._regression_promotion_result(
380
+ source=copy.deepcopy(dict(source)),
381
+ source_path=Path(source_path).expanduser().resolve(),
382
+ name=name,
383
+ min_level=str(min_level),
384
+ max_findings=int(max_findings),
385
+ required_env=list(required_env),
386
+ duration_seconds=round(time.time() - started, 4),
387
+ )
388
+
389
+
390
+ def shrink_attack_evolution_file(
391
+ path: str | Path,
392
+ *,
393
+ name: Optional[str] = None,
394
+ manifest_name: Optional[str] = None,
395
+ required_env: Sequence[str] = (),
396
+ ) -> Dict[str, Any]:
397
+ """Load an attack-evolution artifact and return a minimized replay manifest."""
398
+
399
+ source_path = Path(path).expanduser().resolve()
400
+ return shrink_attack_evolution(
401
+ load_manifest_file(source_path),
402
+ source_path=source_path,
403
+ name=name,
404
+ manifest_name=manifest_name,
405
+ required_env=required_env,
406
+ )
407
+
408
+
409
+ def shrink_attack_evolution(
410
+ source: Mapping[str, Any],
411
+ *,
412
+ source_path: str | Path = ".",
413
+ name: Optional[str] = None,
414
+ manifest_name: Optional[str] = None,
415
+ required_env: Sequence[str] = (),
416
+ ) -> Dict[str, Any]:
417
+ """Return a minimized attack-evolution regression payload."""
418
+
419
+ started = time.time()
420
+ return _cli()._attack_evolution_shrink_result(
421
+ source=copy.deepcopy(dict(source)),
422
+ source_path=Path(source_path).expanduser().resolve(),
423
+ name=name,
424
+ manifest_name=manifest_name,
425
+ required_env=list(required_env),
426
+ duration_seconds=round(time.time() - started, 4),
427
+ )
428
+
429
+
430
+ def replay_manifests(
431
+ manifests: Sequence[str | Path],
432
+ *,
433
+ name: Optional[str] = None,
434
+ dry_run: bool = False,
435
+ fail_fast: bool = False,
436
+ ) -> Dict[str, Any]:
437
+ """Run files/directories/globs as one manifest replay suite."""
438
+
439
+ cli = _cli()
440
+ started = time.time()
441
+ requested = [str(item) for item in manifests]
442
+ paths = cli._replay_manifest_paths(requested)
443
+ children = []
444
+ for path in paths:
445
+ child = cli._execute_replay_manifest(path, dry_run=bool(dry_run))
446
+ children.append(child)
447
+ if child.get("exit_code") != 0 and fail_fast:
448
+ break
449
+ return cli._replay_result(
450
+ children=children,
451
+ requested=requested,
452
+ name=name,
453
+ duration_seconds=round(time.time() - started, 4),
454
+ dry_run=bool(dry_run),
455
+ fail_fast=bool(fail_fast),
456
+ )
457
+
458
+
459
+ async def run_manifest_file(
460
+ path: str | Path,
461
+ *,
462
+ options: Optional[ManifestRunOptions] = None,
463
+ name: Optional[str] = None,
464
+ threshold: Optional[float] = None,
465
+ no_eval: Optional[bool] = None,
466
+ dry_run: Optional[bool] = None,
467
+ ) -> Dict[str, Any]:
468
+ """Load and execute a run manifest, returning the CLI-compatible payload."""
469
+
470
+ manifest_path = Path(path).expanduser().resolve()
471
+ return await run_manifest(
472
+ load_manifest_file(manifest_path),
473
+ manifest_path=manifest_path,
474
+ options=_run_options(
475
+ options,
476
+ name=name,
477
+ threshold=threshold,
478
+ no_eval=no_eval,
479
+ dry_run=dry_run,
480
+ ),
481
+ )
482
+
483
+
484
+ async def run_manifest(
485
+ manifest: Mapping[str, Any],
486
+ *,
487
+ manifest_path: str | Path = ".",
488
+ options: Optional[ManifestRunOptions] = None,
489
+ name: Optional[str] = None,
490
+ threshold: Optional[float] = None,
491
+ no_eval: Optional[bool] = None,
492
+ dry_run: Optional[bool] = None,
493
+ ) -> Dict[str, Any]:
494
+ """Execute a run manifest without writing CLI artifacts."""
495
+
496
+ cli = _cli()
497
+ opts = _run_options(
498
+ options,
499
+ name=name,
500
+ threshold=threshold,
501
+ no_eval=no_eval,
502
+ dry_run=dry_run,
503
+ )
504
+ runtime_manifest = copy.deepcopy(dict(manifest))
505
+ manifest_path = Path(manifest_path).expanduser().resolve()
506
+ if opts.name:
507
+ runtime_manifest["name"] = opts.name
508
+ if opts.threshold is not None:
509
+ runtime_manifest.setdefault("evaluation", {}).setdefault("agent_report", {})[
510
+ "threshold"
511
+ ] = opts.threshold
512
+ if opts.no_eval:
513
+ runtime_manifest.setdefault("evaluation", {})["enabled"] = False
514
+
515
+ started = time.time()
516
+ validate_manifest_env(runtime_manifest)
517
+ apply_manifest_env(runtime_manifest)
518
+ if opts.dry_run:
519
+ return {
520
+ "schema_version": CLI_SCHEMA_VERSION,
521
+ "name": str(runtime_manifest.get("name") or manifest_path.stem),
522
+ "status": "passed",
523
+ "exit_code": 0,
524
+ "dry_run": True,
525
+ "summary": {
526
+ "required_env": required_manifest_env(runtime_manifest),
527
+ "scenario_cases": len(
528
+ cli._scenario_dataset(runtime_manifest, manifest_path.parent)
529
+ ),
530
+ "environment_count": len(cli._environment_specs(runtime_manifest)),
531
+ },
532
+ "duration_seconds": round(time.time() - started, 4),
533
+ }
534
+
535
+ report = await cli._run_manifest(runtime_manifest, manifest_path)
536
+ evaluation = evaluate_manifest_report(runtime_manifest, report)
537
+ result = cli._run_result(
538
+ manifest=runtime_manifest,
539
+ report=report,
540
+ evaluation=evaluation,
541
+ duration_seconds=round(time.time() - started, 4),
542
+ )
543
+ orchestration_strategy = cli._orchestration_strategy_card(
544
+ result,
545
+ source_path=manifest_path,
546
+ source_manifest_path=manifest_path,
547
+ )
548
+ if orchestration_strategy is not None:
549
+ result["orchestration_strategy"] = orchestration_strategy
550
+ framework_readiness = cli._framework_readiness_card(
551
+ result,
552
+ source_path=manifest_path,
553
+ source_manifest_path=manifest_path,
554
+ )
555
+ if framework_readiness is not None:
556
+ result["framework_readiness"] = framework_readiness
557
+ agent_integration_readiness = cli._agent_integration_readiness_card(
558
+ result,
559
+ source_path=manifest_path,
560
+ source_manifest_path=manifest_path,
561
+ )
562
+ if agent_integration_readiness is not None:
563
+ result["agent_integration_readiness"] = agent_integration_readiness
564
+ return result
565
+
566
+
567
+ async def redteam_manifest_file(
568
+ path: str | Path,
569
+ *,
570
+ options: Optional[ManifestRunOptions] = None,
571
+ name: Optional[str] = None,
572
+ threshold: Optional[float] = None,
573
+ dry_run: Optional[bool] = None,
574
+ ) -> Dict[str, Any]:
575
+ """Load and execute a red-team manifest, returning the CLI-compatible payload."""
576
+
577
+ manifest_path = Path(path).expanduser().resolve()
578
+ return await redteam_manifest(
579
+ load_manifest_file(manifest_path),
580
+ manifest_path=manifest_path,
581
+ options=_run_options(
582
+ options,
583
+ name=name,
584
+ threshold=threshold,
585
+ dry_run=dry_run,
586
+ ),
587
+ )
588
+
589
+
590
+ run_redteam_manifest_file = redteam_manifest_file
591
+
592
+
593
+ async def redteam_manifest(
594
+ manifest: Mapping[str, Any],
595
+ *,
596
+ manifest_path: str | Path = ".",
597
+ options: Optional[ManifestRunOptions] = None,
598
+ name: Optional[str] = None,
599
+ threshold: Optional[float] = None,
600
+ dry_run: Optional[bool] = None,
601
+ ) -> Dict[str, Any]:
602
+ """Execute a red-team manifest without writing CLI artifacts."""
603
+
604
+ cli = _cli()
605
+ opts = _run_options(
606
+ options,
607
+ name=name,
608
+ threshold=threshold,
609
+ dry_run=dry_run,
610
+ )
611
+ runtime_manifest = copy.deepcopy(dict(manifest))
612
+ manifest_path = Path(manifest_path).expanduser().resolve()
613
+ if opts.name:
614
+ runtime_manifest["name"] = opts.name
615
+ if opts.threshold is not None:
616
+ runtime_manifest.setdefault("evaluation", {}).setdefault("agent_report", {})[
617
+ "threshold"
618
+ ] = opts.threshold
619
+
620
+ started = time.time()
621
+ redteam_summary = cli._prepare_redteam_manifest(runtime_manifest)
622
+ validate_manifest_env(runtime_manifest)
623
+ apply_manifest_env(runtime_manifest)
624
+ if opts.dry_run:
625
+ result = {
626
+ "schema_version": CLI_SCHEMA_VERSION,
627
+ "name": str(runtime_manifest.get("name") or manifest_path.stem),
628
+ "status": "passed",
629
+ "exit_code": 0,
630
+ "dry_run": True,
631
+ "summary": {
632
+ "required_env": required_manifest_env(runtime_manifest),
633
+ "scenario_cases": len(
634
+ cli._scenario_dataset(runtime_manifest, manifest_path.parent)
635
+ ),
636
+ "environment_count": len(cli._environment_specs(runtime_manifest)),
637
+ "redteam": redteam_summary,
638
+ },
639
+ "redteam": redteam_summary,
640
+ "duration_seconds": round(time.time() - started, 4),
641
+ }
642
+ redteam_strategy = cli._redteam_strategy_card(
643
+ result,
644
+ source_path=manifest_path,
645
+ source_manifest_path=manifest_path,
646
+ )
647
+ if redteam_strategy is not None:
648
+ result["redteam_strategy"] = redteam_strategy
649
+ return result
650
+
651
+ report = await run_local_text_manifest(runtime_manifest, manifest_path)
652
+ evaluation = evaluate_manifest_report(runtime_manifest, report)
653
+ result = cli._run_result(
654
+ manifest=runtime_manifest,
655
+ report=report,
656
+ evaluation=evaluation,
657
+ duration_seconds=round(time.time() - started, 4),
658
+ )
659
+ redteam_result = cli._redteam_result_summary(
660
+ runtime_manifest,
661
+ result.get("evaluation"),
662
+ )
663
+ result["redteam"] = redteam_result
664
+ result["summary"]["redteam"] = redteam_result
665
+ redteam_strategy = cli._redteam_strategy_card(
666
+ result,
667
+ source_path=manifest_path,
668
+ source_manifest_path=manifest_path,
669
+ )
670
+ if redteam_strategy is not None:
671
+ result["redteam_strategy"] = redteam_strategy
672
+ return result
673
+
674
+
675
+ run_redteam_manifest = redteam_manifest
676
+
677
+
678
+ def optimize_manifest_file(
679
+ path: str | Path,
680
+ *,
681
+ options: Optional[ManifestOptimizationOptions] = None,
682
+ name: Optional[str] = None,
683
+ threshold: Optional[float] = None,
684
+ max_candidates: Optional[int] = None,
685
+ dry_run: Optional[bool] = None,
686
+ ) -> Dict[str, Any]:
687
+ """Load and optimize a manifest, returning the CLI-compatible payload."""
688
+
689
+ manifest_path = Path(path).expanduser().resolve()
690
+ return optimize_manifest(
691
+ load_manifest_file(manifest_path),
692
+ manifest_path=manifest_path,
693
+ options=_optimization_options(
694
+ options,
695
+ name=name,
696
+ threshold=threshold,
697
+ max_candidates=max_candidates,
698
+ dry_run=dry_run,
699
+ ),
700
+ )
701
+
702
+
703
+ def optimize_manifest(
704
+ manifest: Mapping[str, Any],
705
+ *,
706
+ manifest_path: str | Path = ".",
707
+ options: Optional[ManifestOptimizationOptions] = None,
708
+ name: Optional[str] = None,
709
+ threshold: Optional[float] = None,
710
+ max_candidates: Optional[int] = None,
711
+ dry_run: Optional[bool] = None,
712
+ ) -> Dict[str, Any]:
713
+ """Optimize a manifest without writing CLI artifacts."""
714
+
715
+ cli = _cli()
716
+ opts = _optimization_options(
717
+ options,
718
+ name=name,
719
+ threshold=threshold,
720
+ max_candidates=max_candidates,
721
+ dry_run=dry_run,
722
+ )
723
+ runtime_manifest = copy.deepcopy(dict(manifest))
724
+ manifest_path = Path(manifest_path).expanduser().resolve()
725
+ if opts.name:
726
+ runtime_manifest["name"] = opts.name
727
+ if opts.threshold is not None:
728
+ runtime_manifest.setdefault("optimization", {})["threshold"] = opts.threshold
729
+ if opts.max_candidates is not None:
730
+ runtime_manifest.setdefault("optimization", {}).setdefault("optimizer", {})[
731
+ "max_candidates"
732
+ ] = opts.max_candidates
733
+
734
+ started = time.time()
735
+ validate_manifest_env(runtime_manifest)
736
+ apply_manifest_env(runtime_manifest)
737
+ optimization = cli._optimization_config(runtime_manifest)
738
+ redteam_summary = _redteam_optimization_summary(runtime_manifest, cli)
739
+ if opts.dry_run:
740
+ summary = {
741
+ "required_env": required_manifest_env(runtime_manifest),
742
+ "search_path_count": len(
743
+ cli._target_config(optimization).get("search_space", {})
744
+ ),
745
+ "max_candidates": cli._optimizer_config(optimization).get("max_candidates"),
746
+ }
747
+ if redteam_summary is not None:
748
+ summary["redteam"] = redteam_summary
749
+ result = {
750
+ "schema_version": CLI_SCHEMA_VERSION,
751
+ "name": str(runtime_manifest.get("name") or manifest_path.stem),
752
+ "status": "passed",
753
+ "exit_code": 0,
754
+ "dry_run": True,
755
+ "summary": summary,
756
+ "duration_seconds": round(time.time() - started, 4),
757
+ }
758
+ if redteam_summary is not None:
759
+ result["redteam"] = redteam_summary
760
+ return result
761
+
762
+ problem = build_manifest_optimization_problem(
763
+ runtime_manifest,
764
+ manifest_path=manifest_path,
765
+ name=str(runtime_manifest.get("name") or manifest_path.stem),
766
+ )
767
+ result = problem.optimize()
768
+ payload = cli._optimization_result(
769
+ manifest=runtime_manifest,
770
+ manifest_path=manifest_path,
771
+ optimization_result=result,
772
+ threshold=float(optimization.get("threshold", 0.7)),
773
+ duration_seconds=round(time.time() - started, 4),
774
+ )
775
+ if redteam_summary is not None:
776
+ payload["redteam"] = redteam_summary
777
+ payload.setdefault("summary", {})["redteam"] = redteam_summary
778
+ orchestration_strategy = cli._orchestration_strategy_card(
779
+ payload,
780
+ source_path=manifest_path,
781
+ source_manifest_path=manifest_path,
782
+ )
783
+ if orchestration_strategy is not None:
784
+ payload["orchestration_strategy"] = orchestration_strategy
785
+ framework_readiness = cli._framework_readiness_card(
786
+ payload,
787
+ source_path=manifest_path,
788
+ source_manifest_path=manifest_path,
789
+ )
790
+ if framework_readiness is not None:
791
+ payload["framework_readiness"] = framework_readiness
792
+ agent_integration_readiness = cli._agent_integration_readiness_card(
793
+ payload,
794
+ source_path=manifest_path,
795
+ source_manifest_path=manifest_path,
796
+ )
797
+ if agent_integration_readiness is not None:
798
+ payload["agent_integration_readiness"] = agent_integration_readiness
799
+ return payload
800
+
801
+
802
+ def build_manifest_optimization_problem(
803
+ manifest: Mapping[str, Any],
804
+ *,
805
+ manifest_path: str | Path = ".",
806
+ name: Optional[str] = None,
807
+ ) -> Any:
808
+ """Build an Agent Learning ManifestOptimizationProblem for this manifest."""
809
+
810
+ cli = _cli()
811
+ manifest_path = Path(manifest_path).expanduser().resolve()
812
+ runtime_manifest = copy.deepcopy(dict(manifest))
813
+ optimization = cli._optimization_config(runtime_manifest)
814
+ manifest_base = copy.deepcopy(dict(runtime_manifest))
815
+ manifest_base.pop("optimization", None)
816
+
817
+ try:
818
+ from fi.opt import ManifestOptimizationProblem
819
+ except Exception as exc: # pragma: no cover - optional dependency clarity
820
+ raise ManifestError(
821
+ "Agent Learning Kit optimizer engine is required for manifest optimization."
822
+ ) from exc
823
+
824
+ def evaluate_manifest(candidate_manifest: Mapping[str, Any], candidate: Any) -> Any:
825
+ if isinstance(candidate_manifest, dict):
826
+ runtime_candidate = candidate_manifest
827
+ else:
828
+ runtime_candidate = copy.deepcopy(dict(candidate_manifest))
829
+ _prepare_redteam_if_present(runtime_candidate, cli)
830
+ return run_local_text_manifest(runtime_candidate, manifest_path)
831
+
832
+ def score_manifest(
833
+ candidate_manifest: Mapping[str, Any],
834
+ report: Any,
835
+ candidate: Any,
836
+ ) -> Dict[str, Any]:
837
+ evaluation = evaluate_manifest_report(candidate_manifest, report)
838
+ evidence_config = _simulation_evidence_scoring_config(optimization)
839
+ evidence_evaluation = None
840
+ if evidence_config is not None:
841
+ from fi.opt import score_simulation_evidence
842
+
843
+ evidence_evaluation = score_simulation_evidence(
844
+ report,
845
+ manifest=candidate_manifest,
846
+ candidate=candidate,
847
+ config=evidence_config,
848
+ )
849
+ score = float(evidence_evaluation.score)
850
+ else:
851
+ score = float(
852
+ getattr(evaluation, "score", 1.0 if evaluation is None else 0.0)
853
+ )
854
+ # bug #2: when the manifest DECLARES an anchor objective, score the
855
+ # candidate on it (real dynamic range) instead of the all-metrics-mean
856
+ # evaluation score. Scoped: no declared-anchor objective -> unchanged.
857
+ from fi.opt.integrations.simulate import _score_from_value
858
+
859
+ eval_plain = cli._to_plain(evaluation) if evaluation is not None else {}
860
+ objective = (
861
+ candidate_manifest.get("objective")
862
+ or (
863
+ (candidate_manifest.get("simulation") or {}).get("inline") or {}
864
+ ).get("objective")
865
+ or (candidate_manifest.get("evaluation") or {}).get("objective")
866
+ )
867
+ anchored = _score_from_value(
868
+ {
869
+ "objective": objective,
870
+ "summary": (
871
+ eval_plain.get("summary")
872
+ if isinstance(eval_plain, Mapping)
873
+ else {}
874
+ )
875
+ or {},
876
+ }
877
+ )
878
+ if anchored is not None:
879
+ score = float(anchored)
880
+ metadata = {
881
+ "agent_report_evaluation": (
882
+ cli._to_plain(evaluation) if evaluation is not None else None
883
+ ),
884
+ "report_summary": cli._report_summary(report),
885
+ }
886
+ if evidence_evaluation is not None:
887
+ metadata["simulation_evidence_score"] = evidence_evaluation.metadata.get(
888
+ "simulation_evidence_score"
889
+ )
890
+ return {
891
+ "score": score,
892
+ "reason": getattr(evidence_evaluation, "reason", "")
893
+ if evidence_evaluation is not None
894
+ else "",
895
+ "metadata": metadata,
896
+ }
897
+
898
+ return ManifestOptimizationProblem.from_manifest(
899
+ {**manifest_base, "optimization": optimization},
900
+ evaluate_manifest=evaluate_manifest,
901
+ score_manifest=score_manifest,
902
+ name=name or str(manifest.get("name") or manifest_path.stem),
903
+ )
904
+
905
+
906
+ def _simulation_evidence_scoring_config(
907
+ optimization: Mapping[str, Any],
908
+ ) -> Optional[Dict[str, Any]]:
909
+ raw = (
910
+ optimization.get("simulation_evidence")
911
+ or optimization.get("evidence_scorer")
912
+ or optimization.get("scoring")
913
+ )
914
+ if raw is True:
915
+ return {"enabled": True, "method": "simulation_evidence"}
916
+ if isinstance(raw, str):
917
+ normalized = raw.strip().lower().replace("-", "_").replace(" ", "_")
918
+ if normalized in {"simulation_evidence", "evidence", "environment_evidence"}:
919
+ return {"enabled": True, "method": "simulation_evidence"}
920
+ return None
921
+ if not isinstance(raw, Mapping):
922
+ return None
923
+ method = (
924
+ str(
925
+ raw.get("method")
926
+ or raw.get("type")
927
+ or raw.get("name")
928
+ or raw.get("strategy")
929
+ or "simulation_evidence"
930
+ )
931
+ .strip()
932
+ .lower()
933
+ .replace("-", "_")
934
+ .replace(" ", "_")
935
+ )
936
+ if not bool(raw.get("enabled", True)):
937
+ return None
938
+ if method not in {
939
+ "simulation_evidence",
940
+ "evidence",
941
+ "environment_evidence",
942
+ "trace_evidence",
943
+ }:
944
+ return None
945
+ config = copy.deepcopy(dict(raw))
946
+ config["method"] = "simulation_evidence"
947
+ return config
948
+
949
+
950
+ def _cli() -> Any:
951
+ return importlib.import_module("fi.simulate.cli")
952
+
953
+
954
+ def _run_options(
955
+ options: Optional[ManifestRunOptions],
956
+ *,
957
+ name: Optional[str] = None,
958
+ threshold: Optional[float] = None,
959
+ no_eval: Optional[bool] = None,
960
+ dry_run: Optional[bool] = None,
961
+ ) -> ManifestRunOptions:
962
+ opts = options or ManifestRunOptions()
963
+ return ManifestRunOptions(
964
+ name=opts.name if name is None else name,
965
+ threshold=opts.threshold if threshold is None else threshold,
966
+ no_eval=opts.no_eval if no_eval is None else no_eval,
967
+ dry_run=opts.dry_run if dry_run is None else dry_run,
968
+ )
969
+
970
+
971
+ def _optimization_options(
972
+ options: Optional[ManifestOptimizationOptions],
973
+ *,
974
+ name: Optional[str] = None,
975
+ threshold: Optional[float] = None,
976
+ max_candidates: Optional[int] = None,
977
+ dry_run: Optional[bool] = None,
978
+ ) -> ManifestOptimizationOptions:
979
+ opts = options or ManifestOptimizationOptions()
980
+ return ManifestOptimizationOptions(
981
+ name=opts.name if name is None else name,
982
+ threshold=opts.threshold if threshold is None else threshold,
983
+ max_candidates=opts.max_candidates
984
+ if max_candidates is None
985
+ else max_candidates,
986
+ dry_run=opts.dry_run if dry_run is None else dry_run,
987
+ )
988
+
989
+
990
+ __all__ = [
991
+ "CLI_SCHEMA_VERSION",
992
+ "MANIFEST_SCHEMA_VERSION",
993
+ "ManifestError",
994
+ "ManifestOptimizationOptions",
995
+ "ManifestRunOptions",
996
+ "apply_manifest_env",
997
+ "build_manifest_agent_callback",
998
+ "build_manifest_environments",
999
+ "build_manifest_optimization_problem",
1000
+ "compare_result_files",
1001
+ "compare_results",
1002
+ "create_baseline",
1003
+ "create_baseline_file",
1004
+ "detect_manifest_command",
1005
+ "evaluate_manifest_report",
1006
+ "load_manifest",
1007
+ "load_manifest_file",
1008
+ "missing_manifest_env",
1009
+ "optimize_manifest",
1010
+ "optimize_manifest_file",
1011
+ "prepare_redteam_manifest",
1012
+ "promote_to_regression",
1013
+ "promote_to_regression_file",
1014
+ "public_result",
1015
+ "redteam_manifest",
1016
+ "redteam_manifest_file",
1017
+ "required_manifest_env",
1018
+ "render_junit",
1019
+ "render_markdown",
1020
+ "render_report",
1021
+ "render_report_file",
1022
+ "render_sarif",
1023
+ "replay_manifests",
1024
+ "run_local_text_manifest",
1025
+ "run_manifest",
1026
+ "run_manifest_file",
1027
+ "run_redteam_manifest",
1028
+ "run_redteam_manifest_file",
1029
+ "shrink_attack_evolution",
1030
+ "shrink_attack_evolution_file",
1031
+ "supported_manifest_environment_types",
1032
+ "validate_manifest_env",
1033
+ ]