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,211 @@
1
+ """Application analyzer for AutoEval.
2
+
3
+ Analyzes application descriptions to understand evaluation needs.
4
+ Supports both LLM-powered and rule-based analysis.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ from typing import Optional, Any, Protocol, runtime_checkable
10
+
11
+ from .types import (
12
+ AppAnalysis,
13
+ AppCategory,
14
+ RiskLevel,
15
+ DomainSensitivity,
16
+ AppRequirement,
17
+ )
18
+ from .rules import RuleBasedAnalyzer
19
+ from .prompts import ANALYSIS_SYSTEM_PROMPT, ANALYSIS_USER_PROMPT
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ @runtime_checkable
25
+ class LLMProvider(Protocol):
26
+ """Protocol for LLM providers."""
27
+
28
+ def complete(
29
+ self,
30
+ prompt: str,
31
+ system_prompt: Optional[str] = None,
32
+ **kwargs: Any,
33
+ ) -> str:
34
+ """Generate a completion for the given prompt."""
35
+ ...
36
+
37
+
38
+ class AppAnalyzer:
39
+ """
40
+ Analyzes application descriptions to understand evaluation needs.
41
+
42
+ Supports both LLM-powered and rule-based analysis. When LLM analysis
43
+ fails, automatically falls back to rule-based analysis.
44
+
45
+ Example:
46
+ # With LLM provider
47
+ from fi.evals.llm import LiteLLMProvider
48
+ analyzer = AppAnalyzer(llm_provider=LiteLLMProvider())
49
+ analysis = analyzer.analyze(
50
+ "A customer support chatbot for a healthcare company. "
51
+ "It retrieves patient information and answers questions."
52
+ )
53
+
54
+ # Without LLM (rule-based only)
55
+ analyzer = AppAnalyzer(use_llm=False)
56
+ analysis = analyzer.analyze("A RAG-based document Q&A system.")
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ llm_provider: Optional[Any] = None,
62
+ model: str = "gpt-4o-mini",
63
+ use_llm: bool = True,
64
+ temperature: float = 0.1,
65
+ ):
66
+ """
67
+ Initialize the analyzer.
68
+
69
+ Args:
70
+ llm_provider: LLM provider for intelligent analysis (optional)
71
+ model: Model to use for LLM analysis
72
+ use_llm: If False, always use rule-based fallback
73
+ temperature: Temperature for LLM generation (lower = more deterministic)
74
+ """
75
+ self.llm_provider = llm_provider
76
+ self.model = model
77
+ self.use_llm = use_llm and llm_provider is not None
78
+ self.temperature = temperature
79
+ self.rule_analyzer = RuleBasedAnalyzer()
80
+
81
+ def analyze(self, description: str) -> AppAnalysis:
82
+ """
83
+ Analyze an application description.
84
+
85
+ Args:
86
+ description: Natural language description of the application
87
+
88
+ Returns:
89
+ AppAnalysis with detected requirements and recommendations
90
+ """
91
+ if not description or not description.strip():
92
+ return AppAnalysis(
93
+ category=AppCategory.UNKNOWN,
94
+ risk_level=RiskLevel.MEDIUM,
95
+ domain_sensitivity=DomainSensitivity.GENERAL,
96
+ requirements=[],
97
+ detected_features=[],
98
+ confidence=0.0,
99
+ explanation="Empty description provided.",
100
+ )
101
+
102
+ if self.use_llm:
103
+ try:
104
+ return self._analyze_with_llm(description)
105
+ except Exception as e:
106
+ logger.warning(f"LLM analysis failed, falling back to rules: {e}")
107
+ return self._analyze_with_rules(description)
108
+ else:
109
+ return self._analyze_with_rules(description)
110
+
111
+ def _analyze_with_llm(self, description: str) -> AppAnalysis:
112
+ """Use LLM to analyze the description."""
113
+ if not self.llm_provider:
114
+ raise ValueError("LLM provider not configured")
115
+
116
+ prompt = ANALYSIS_USER_PROMPT.format(description=description)
117
+
118
+ # Try different ways to call the LLM provider
119
+ try:
120
+ if hasattr(self.llm_provider, "complete"):
121
+ response = self.llm_provider.complete(
122
+ prompt=prompt,
123
+ system_prompt=ANALYSIS_SYSTEM_PROMPT,
124
+ model=self.model,
125
+ temperature=self.temperature,
126
+ )
127
+ elif hasattr(self.llm_provider, "generate"):
128
+ response = self.llm_provider.generate(
129
+ prompt=prompt,
130
+ system_prompt=ANALYSIS_SYSTEM_PROMPT,
131
+ model=self.model,
132
+ temperature=self.temperature,
133
+ )
134
+ elif callable(self.llm_provider):
135
+ response = self.llm_provider(
136
+ prompt=prompt,
137
+ system_prompt=ANALYSIS_SYSTEM_PROMPT,
138
+ )
139
+ else:
140
+ raise ValueError(f"Unknown LLM provider type: {type(self.llm_provider)}")
141
+ except TypeError as e:
142
+ # Only retry with simpler args if it's a signature mismatch
143
+ if "unexpected keyword argument" in str(e) or "positional argument" in str(e):
144
+ if hasattr(self.llm_provider, "complete"):
145
+ response = self.llm_provider.complete(prompt)
146
+ else:
147
+ raise
148
+ else:
149
+ raise
150
+
151
+ # Parse JSON response
152
+ return self._parse_llm_response(response, description)
153
+
154
+ def _parse_llm_response(self, response: str, description: str) -> AppAnalysis:
155
+ """Parse LLM response into AppAnalysis."""
156
+ # Clean up response - remove markdown code blocks if present
157
+ response = response.strip()
158
+ if response.startswith("```"):
159
+ lines = response.split("\n")
160
+ response = "\n".join(lines[1:-1] if lines[-1] == "```" else lines[1:])
161
+
162
+ try:
163
+ data = json.loads(response)
164
+ except json.JSONDecodeError as e:
165
+ logger.warning(f"Failed to parse LLM response as JSON: {e}")
166
+ # Fall back to rule-based
167
+ return self._analyze_with_rules(description)
168
+
169
+ # Parse with defaults
170
+ try:
171
+ category = AppCategory(data.get("category", "unknown"))
172
+ except ValueError:
173
+ category = AppCategory.UNKNOWN
174
+
175
+ try:
176
+ risk_level = RiskLevel(data.get("risk_level", "medium"))
177
+ except ValueError:
178
+ risk_level = RiskLevel.MEDIUM
179
+
180
+ try:
181
+ domain_sensitivity = DomainSensitivity(
182
+ data.get("domain_sensitivity", "general")
183
+ )
184
+ except ValueError:
185
+ domain_sensitivity = DomainSensitivity.GENERAL
186
+
187
+ requirements = []
188
+ for req_data in data.get("requirements", []):
189
+ requirements.append(
190
+ AppRequirement(
191
+ category=req_data.get("category", ""),
192
+ importance=req_data.get("importance", "recommended"),
193
+ reason=req_data.get("reason", ""),
194
+ suggested_evals=req_data.get("suggested_evals", []),
195
+ suggested_scanners=req_data.get("suggested_scanners", []),
196
+ )
197
+ )
198
+
199
+ return AppAnalysis(
200
+ category=category,
201
+ risk_level=risk_level,
202
+ domain_sensitivity=domain_sensitivity,
203
+ requirements=requirements,
204
+ detected_features=data.get("detected_features", []),
205
+ confidence=0.85, # LLM analysis has higher confidence
206
+ explanation=data.get("explanation", "Analysis performed by LLM."),
207
+ )
208
+
209
+ def _analyze_with_rules(self, description: str) -> AppAnalysis:
210
+ """Use rule-based analysis."""
211
+ return self.rule_analyzer.analyze(description)
@@ -0,0 +1,244 @@
1
+ """Configuration classes for AutoEval."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Dict, Any, Optional
5
+ from copy import deepcopy
6
+
7
+
8
+ @dataclass
9
+ class EvalConfig:
10
+ """Configuration for a single evaluation."""
11
+
12
+ name: str
13
+ enabled: bool = True
14
+ threshold: float = 0.7
15
+ weight: float = 1.0
16
+ params: Dict[str, Any] = field(default_factory=dict)
17
+ model: Optional[str] = None # LLM model for augmented metrics
18
+ augment: bool = False # Enable local→LLM augmentation
19
+
20
+ def to_dict(self) -> Dict[str, Any]:
21
+ """Convert to dictionary."""
22
+ result = {
23
+ "name": self.name,
24
+ "enabled": self.enabled,
25
+ "threshold": self.threshold,
26
+ "weight": self.weight,
27
+ }
28
+ if self.params:
29
+ result["params"] = self.params
30
+ if self.model:
31
+ result["model"] = self.model
32
+ if self.augment:
33
+ result["augment"] = self.augment
34
+ return result
35
+
36
+ @classmethod
37
+ def from_dict(cls, data: Dict[str, Any]) -> "EvalConfig":
38
+ """Create from dictionary."""
39
+ return cls(
40
+ name=data["name"],
41
+ enabled=data.get("enabled", True),
42
+ threshold=data.get("threshold", 0.7),
43
+ weight=data.get("weight", 1.0),
44
+ params=data.get("params", {}),
45
+ model=data.get("model"),
46
+ augment=data.get("augment", False),
47
+ )
48
+
49
+ def copy(self) -> "EvalConfig":
50
+ """Create a deep copy."""
51
+ return EvalConfig(
52
+ name=self.name,
53
+ enabled=self.enabled,
54
+ threshold=self.threshold,
55
+ weight=self.weight,
56
+ params=deepcopy(self.params),
57
+ model=self.model,
58
+ augment=self.augment,
59
+ )
60
+
61
+
62
+ @dataclass
63
+ class ScannerConfig:
64
+ """Configuration for a single scanner."""
65
+
66
+ name: str
67
+ enabled: bool = True
68
+ threshold: float = 0.7
69
+ action: str = "block" # block, flag, warn, redact
70
+ params: Dict[str, Any] = field(default_factory=dict)
71
+
72
+ def to_dict(self) -> Dict[str, Any]:
73
+ """Convert to dictionary."""
74
+ result = {
75
+ "name": self.name,
76
+ "enabled": self.enabled,
77
+ "threshold": self.threshold,
78
+ "action": self.action,
79
+ }
80
+ if self.params:
81
+ result["params"] = self.params
82
+ return result
83
+
84
+ @classmethod
85
+ def from_dict(cls, data: Dict[str, Any]) -> "ScannerConfig":
86
+ """Create from dictionary."""
87
+ return cls(
88
+ name=data["name"],
89
+ enabled=data.get("enabled", True),
90
+ threshold=data.get("threshold", 0.7),
91
+ action=data.get("action", "block"),
92
+ params=data.get("params", {}),
93
+ )
94
+
95
+ def copy(self) -> "ScannerConfig":
96
+ """Create a deep copy."""
97
+ return ScannerConfig(
98
+ name=self.name,
99
+ enabled=self.enabled,
100
+ threshold=self.threshold,
101
+ action=self.action,
102
+ params=deepcopy(self.params),
103
+ )
104
+
105
+
106
+ @dataclass
107
+ class AutoEvalConfig:
108
+ """Complete configuration for an auto-generated evaluation pipeline."""
109
+
110
+ name: str
111
+ description: str = ""
112
+ version: str = "1.0.0"
113
+
114
+ # Analysis metadata
115
+ app_category: str = "unknown"
116
+ risk_level: str = "medium"
117
+ domain_sensitivity: str = "general"
118
+
119
+ # Evaluations
120
+ evaluations: List[EvalConfig] = field(default_factory=list)
121
+
122
+ # Scanners
123
+ scanners: List[ScannerConfig] = field(default_factory=list)
124
+
125
+ # Execution settings
126
+ execution_mode: str = "non_blocking" # blocking, non_blocking, distributed
127
+ parallel_workers: int = 4
128
+ timeout_seconds: int = 30
129
+ fail_fast: bool = False
130
+
131
+ # Thresholds
132
+ global_pass_rate: float = 0.8
133
+
134
+ def to_dict(self) -> Dict[str, Any]:
135
+ """Convert to dictionary for export."""
136
+ return {
137
+ "version": self.version,
138
+ "name": self.name,
139
+ "description": self.description,
140
+ "metadata": {
141
+ "app_category": self.app_category,
142
+ "risk_level": self.risk_level,
143
+ "domain_sensitivity": self.domain_sensitivity,
144
+ "generated_by": "autoeval",
145
+ },
146
+ "execution": {
147
+ "mode": self.execution_mode,
148
+ "parallel_workers": self.parallel_workers,
149
+ "timeout_seconds": self.timeout_seconds,
150
+ "fail_fast": self.fail_fast,
151
+ },
152
+ "thresholds": {
153
+ "global_pass_rate": self.global_pass_rate,
154
+ },
155
+ "evaluations": [e.to_dict() for e in self.evaluations],
156
+ "scanners": [s.to_dict() for s in self.scanners],
157
+ }
158
+
159
+ @classmethod
160
+ def from_dict(cls, data: Dict[str, Any]) -> "AutoEvalConfig":
161
+ """Create from dictionary (YAML import)."""
162
+ metadata = data.get("metadata", {})
163
+ execution = data.get("execution", {})
164
+ thresholds = data.get("thresholds", {})
165
+
166
+ return cls(
167
+ name=data.get("name", "unnamed"),
168
+ description=data.get("description", ""),
169
+ version=data.get("version", "1.0.0"),
170
+ app_category=metadata.get("app_category", "unknown"),
171
+ risk_level=metadata.get("risk_level", "medium"),
172
+ domain_sensitivity=metadata.get("domain_sensitivity", "general"),
173
+ evaluations=[
174
+ EvalConfig.from_dict(e) for e in data.get("evaluations", [])
175
+ ],
176
+ scanners=[
177
+ ScannerConfig.from_dict(s) for s in data.get("scanners", [])
178
+ ],
179
+ execution_mode=execution.get("mode", "non_blocking"),
180
+ parallel_workers=execution.get("parallel_workers", 4),
181
+ timeout_seconds=execution.get("timeout_seconds", 30),
182
+ fail_fast=execution.get("fail_fast", False),
183
+ global_pass_rate=thresholds.get("global_pass_rate", 0.8),
184
+ )
185
+
186
+ def copy(self) -> "AutoEvalConfig":
187
+ """Create a deep copy of the configuration."""
188
+ return AutoEvalConfig(
189
+ name=self.name,
190
+ description=self.description,
191
+ version=self.version,
192
+ app_category=self.app_category,
193
+ risk_level=self.risk_level,
194
+ domain_sensitivity=self.domain_sensitivity,
195
+ evaluations=[e.copy() for e in self.evaluations],
196
+ scanners=[s.copy() for s in self.scanners],
197
+ execution_mode=self.execution_mode,
198
+ parallel_workers=self.parallel_workers,
199
+ timeout_seconds=self.timeout_seconds,
200
+ fail_fast=self.fail_fast,
201
+ global_pass_rate=self.global_pass_rate,
202
+ )
203
+
204
+ def get_eval(self, name: str) -> Optional[EvalConfig]:
205
+ """Get evaluation config by name."""
206
+ for e in self.evaluations:
207
+ if e.name == name:
208
+ return e
209
+ return None
210
+
211
+ def get_scanner(self, name: str) -> Optional[ScannerConfig]:
212
+ """Get scanner config by name."""
213
+ for s in self.scanners:
214
+ if s.name == name:
215
+ return s
216
+ return None
217
+
218
+ def summary(self) -> str:
219
+ """Get a summary of the configuration."""
220
+ lines = [
221
+ f"AutoEval Config: {self.name}",
222
+ f" Category: {self.app_category}",
223
+ f" Risk Level: {self.risk_level}",
224
+ f" Domain: {self.domain_sensitivity}",
225
+ "",
226
+ f"Evaluations ({len(self.evaluations)}):",
227
+ ]
228
+
229
+ for e in self.evaluations:
230
+ status = "enabled" if e.enabled else "disabled"
231
+ lines.append(f" - {e.name} (threshold: {e.threshold}, {status})")
232
+
233
+ lines.append("")
234
+ lines.append(f"Scanners ({len(self.scanners)}):")
235
+
236
+ for s in self.scanners:
237
+ status = "enabled" if s.enabled else "disabled"
238
+ lines.append(f" - {s.name} (action: {s.action}, {status})")
239
+
240
+ lines.append("")
241
+ lines.append(f"Execution: {self.execution_mode}, workers: {self.parallel_workers}")
242
+ lines.append(f"Pass Rate Threshold: {self.global_pass_rate:.0%}")
243
+
244
+ return "\n".join(lines)
@@ -0,0 +1,213 @@
1
+ """Export and import functionality for AutoEval configurations.
2
+
3
+ Supports YAML and JSON formats for version-controlled configurations.
4
+ """
5
+
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Union
9
+
10
+ from .config import AutoEvalConfig
11
+
12
+
13
+ def export_yaml(config: AutoEvalConfig, path: Union[str, Path]) -> None:
14
+ """
15
+ Export configuration to YAML file.
16
+
17
+ Args:
18
+ config: AutoEvalConfig to export
19
+ path: Path to write YAML file
20
+
21
+ Example:
22
+ export_yaml(config, "eval_config.yaml")
23
+ """
24
+ try:
25
+ import yaml
26
+ except ImportError:
27
+ raise ImportError("PyYAML is required for YAML export. Install with: pip install pyyaml")
28
+
29
+ data = config.to_dict()
30
+ path = Path(path)
31
+
32
+ with open(path, "w") as f:
33
+ yaml.dump(data, f, default_flow_style=False, sort_keys=False, allow_unicode=True)
34
+
35
+
36
+ def export_json(config: AutoEvalConfig, path: Union[str, Path], indent: int = 2) -> None:
37
+ """
38
+ Export configuration to JSON file.
39
+
40
+ Args:
41
+ config: AutoEvalConfig to export
42
+ path: Path to write JSON file
43
+ indent: Indentation level for pretty printing
44
+
45
+ Example:
46
+ export_json(config, "eval_config.json")
47
+ """
48
+ data = config.to_dict()
49
+ path = Path(path)
50
+
51
+ with open(path, "w") as f:
52
+ json.dump(data, f, indent=indent)
53
+
54
+
55
+ def load_yaml(path: Union[str, Path]) -> AutoEvalConfig:
56
+ """
57
+ Load configuration from YAML file.
58
+
59
+ Args:
60
+ path: Path to YAML configuration file
61
+
62
+ Returns:
63
+ AutoEvalConfig instance
64
+
65
+ Example:
66
+ config = load_yaml("eval_config.yaml")
67
+ """
68
+ try:
69
+ import yaml
70
+ except ImportError:
71
+ raise ImportError("PyYAML is required for YAML import. Install with: pip install pyyaml")
72
+
73
+ path = Path(path)
74
+
75
+ with open(path, "r") as f:
76
+ data = yaml.safe_load(f)
77
+
78
+ return AutoEvalConfig.from_dict(data)
79
+
80
+
81
+ def load_json(path: Union[str, Path]) -> AutoEvalConfig:
82
+ """
83
+ Load configuration from JSON file.
84
+
85
+ Args:
86
+ path: Path to JSON configuration file
87
+
88
+ Returns:
89
+ AutoEvalConfig instance
90
+
91
+ Example:
92
+ config = load_json("eval_config.json")
93
+ """
94
+ path = Path(path)
95
+
96
+ with open(path, "r") as f:
97
+ data = json.load(f)
98
+
99
+ return AutoEvalConfig.from_dict(data)
100
+
101
+
102
+ def load_config(path: Union[str, Path]) -> AutoEvalConfig:
103
+ """
104
+ Load configuration from file (auto-detect format).
105
+
106
+ Determines format from file extension (.yaml, .yml, .json).
107
+
108
+ Args:
109
+ path: Path to configuration file
110
+
111
+ Returns:
112
+ AutoEvalConfig instance
113
+
114
+ Raises:
115
+ ValueError: If file extension not recognized
116
+
117
+ Example:
118
+ config = load_config("eval_config.yaml")
119
+ config = load_config("eval_config.json")
120
+ """
121
+ path = Path(path)
122
+ suffix = path.suffix.lower()
123
+
124
+ if suffix in {".yaml", ".yml"}:
125
+ return load_yaml(path)
126
+ elif suffix == ".json":
127
+ return load_json(path)
128
+ else:
129
+ raise ValueError(
130
+ f"Unknown file format: {suffix}. "
131
+ "Supported formats: .yaml, .yml, .json"
132
+ )
133
+
134
+
135
+ def to_yaml_string(config: AutoEvalConfig) -> str:
136
+ """
137
+ Convert configuration to YAML string.
138
+
139
+ Args:
140
+ config: AutoEvalConfig to convert
141
+
142
+ Returns:
143
+ YAML string representation
144
+
145
+ Example:
146
+ yaml_str = to_yaml_string(config)
147
+ print(yaml_str)
148
+ """
149
+ try:
150
+ import yaml
151
+ except ImportError:
152
+ raise ImportError("PyYAML is required. Install with: pip install pyyaml")
153
+
154
+ data = config.to_dict()
155
+ return yaml.dump(data, default_flow_style=False, sort_keys=False, allow_unicode=True)
156
+
157
+
158
+ def to_json_string(config: AutoEvalConfig, indent: int = 2) -> str:
159
+ """
160
+ Convert configuration to JSON string.
161
+
162
+ Args:
163
+ config: AutoEvalConfig to convert
164
+ indent: Indentation level for pretty printing
165
+
166
+ Returns:
167
+ JSON string representation
168
+
169
+ Example:
170
+ json_str = to_json_string(config)
171
+ print(json_str)
172
+ """
173
+ data = config.to_dict()
174
+ return json.dumps(data, indent=indent)
175
+
176
+
177
+ def from_yaml_string(yaml_str: str) -> AutoEvalConfig:
178
+ """
179
+ Load configuration from YAML string.
180
+
181
+ Args:
182
+ yaml_str: YAML string to parse
183
+
184
+ Returns:
185
+ AutoEvalConfig instance
186
+
187
+ Example:
188
+ config = from_yaml_string(yaml_content)
189
+ """
190
+ try:
191
+ import yaml
192
+ except ImportError:
193
+ raise ImportError("PyYAML is required. Install with: pip install pyyaml")
194
+
195
+ data = yaml.safe_load(yaml_str)
196
+ return AutoEvalConfig.from_dict(data)
197
+
198
+
199
+ def from_json_string(json_str: str) -> AutoEvalConfig:
200
+ """
201
+ Load configuration from JSON string.
202
+
203
+ Args:
204
+ json_str: JSON string to parse
205
+
206
+ Returns:
207
+ AutoEvalConfig instance
208
+
209
+ Example:
210
+ config = from_json_string(json_content)
211
+ """
212
+ data = json.loads(json_str)
213
+ return AutoEvalConfig.from_dict(data)