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,3 @@
1
+ from .litellm import LiteLLMGenerator
2
+
3
+ __all__ = ["LiteLLMGenerator"]
@@ -0,0 +1,66 @@
1
+ import litellm
2
+ from typing import Dict
3
+
4
+ from ..types import LLMMessage
5
+ from ..base.base_generator import BaseGenerator
6
+
7
+ # logging.getLogger("LiteLLM").setLevel(logging.WARNING)
8
+
9
+
10
+ class LiteLLMGenerator(BaseGenerator):
11
+ """
12
+ A Generator that uses LiteLLM to call any supported language model.
13
+ """
14
+
15
+ def __init__(self, model: str, prompt_template: str):
16
+ """
17
+ Initializes the LiteLLMGenerator.
18
+
19
+ Args:
20
+ model: The name of the model to use (e.g., "gpt-4o-mini").
21
+ prompt_template: A string template for the prompt, with placeholders
22
+ in f-string format (e.g., "Summarize this: {text}").
23
+ """
24
+ self.model = model
25
+ self.prompt_template = prompt_template
26
+ # LiteLLM is stateless, so no further setup is needed here.
27
+
28
+ def generate(self, prompt_vars: Dict[str, str], **litellm_kwargs) -> str:
29
+ """
30
+ Fills the prompt template and calls the LiteLLM API.
31
+
32
+ Args:
33
+ prompt_vars: A dictionary of variables to fill the prompt template.
34
+ litellm_kwargs: Any litellm supported kwargs
35
+
36
+ Returns:
37
+ The string content of the model's response.
38
+ """
39
+ prompt = self.prompt_template.format(**prompt_vars)
40
+
41
+ messages = [LLMMessage(role="user", content=prompt)]
42
+ messages_for_litellm = [msg.model_dump(exclude_none=True) for msg in messages]
43
+
44
+ try:
45
+ # litellm allows us to drop any params which may not be supported by the model
46
+ litellm.drop_params = True
47
+ response = litellm.completion(
48
+ model=self.model, messages=messages_for_litellm, **litellm_kwargs
49
+ )
50
+ return response.choices[0].message.content
51
+ except Exception as e:
52
+ # Basic error handling
53
+ print(f"An error occurred with LiteLLM: {e}")
54
+ return ""
55
+
56
+ @property
57
+ def model_name(self) -> str:
58
+ return self.model
59
+
60
+ def get_prompt_template(self) -> str:
61
+ """Returns the current prompt template."""
62
+ return self.prompt_template
63
+
64
+ def set_prompt_template(self, template: str):
65
+ """Updates the prompt template."""
66
+ self.prompt_template = template
@@ -0,0 +1,23 @@
1
+ from .simulate import (
2
+ ManifestOptimizationProblem,
3
+ ManifestRunner,
4
+ ManifestScorer,
5
+ SimulateManifestOptimizationProblem,
6
+ deep_merge,
7
+ optimize_simulate_manifest,
8
+ optimize_simulate_manifest_file,
9
+ problem_from_simulate_manifest,
10
+ problem_from_simulate_manifest_file,
11
+ )
12
+
13
+ __all__ = [
14
+ "ManifestOptimizationProblem",
15
+ "ManifestRunner",
16
+ "ManifestScorer",
17
+ "SimulateManifestOptimizationProblem",
18
+ "deep_merge",
19
+ "optimize_simulate_manifest",
20
+ "optimize_simulate_manifest_file",
21
+ "problem_from_simulate_manifest",
22
+ "problem_from_simulate_manifest_file",
23
+ ]
@@ -0,0 +1,410 @@
1
+ """Generative (LLM prompt-rewriting) optimizer bridge for eval suites.
2
+
3
+ Runs the real optimizers in :mod:`fi.opt.optimizers` — GEPA, ProTeGi,
4
+ MetaPrompt, PromptWizard, RandomSearch, BayesianSearch — against a
5
+ promptfoo-style eval suite. Candidate prompts are produced by a task LLM and
6
+ scored against the suite's own assertions (including ``fi_eval`` platform
7
+ templates), so the optimizer maximizes the suite's pass-rate.
8
+
9
+ Unlike the deterministic agent/target backends wired through
10
+ ``_optimizer_cls`` (which mutate a search space and never rewrite prompt
11
+ text), these optimizers use an LLM to *generate* new prompts. They are opt-in
12
+ via ``optimization.optimizer.algorithm`` set to a generative token.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ import time
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Mapping, Optional, Sequence
20
+
21
+ from ..types import EvaluationResult
22
+
23
+ GENERATIVE_TOKENS = {
24
+ "gepa",
25
+ "protegi",
26
+ "metaprompt",
27
+ "promptwizard",
28
+ "random_search",
29
+ "bayesian_search",
30
+ }
31
+
32
+ _VAR_PLACEHOLDER = re.compile(r"{{\s*([A-Za-z_][\w]*)\s*}}")
33
+
34
+
35
+ def _manifest_error(message: str) -> Exception:
36
+ from fi.simulate.manifest import ManifestError
37
+
38
+ return ManifestError(message)
39
+
40
+
41
+ def _suite_attr(name: str) -> Any:
42
+ """Resolve a public helper from the installed simulate-sdk."""
43
+ try:
44
+ from fi import simulate as simulate_sdk
45
+ except Exception as exc: # pragma: no cover - optional dependency clarity
46
+ raise _manifest_error(
47
+ "agent-simulate is required for generative eval-suite optimization."
48
+ ) from exc
49
+ attr = getattr(simulate_sdk, name, None)
50
+ if attr is None:
51
+ from fi.simulate import suite as suite_mod
52
+
53
+ attr = getattr(suite_mod, name, None)
54
+ if attr is None: # pragma: no cover - version clarity
55
+ raise _manifest_error(
56
+ f"agent-simulate with `{name}` is required; upgrade simulate-sdk."
57
+ )
58
+ return attr
59
+
60
+
61
+ def _to_format_template(template: str) -> str:
62
+ """Convert ``{{ var }}`` placeholders to ``{var}`` for str.format generators."""
63
+ return _VAR_PLACEHOLDER.sub(r"{\1}", template or "")
64
+
65
+
66
+ def _dataset_fields(dataset: Sequence[Mapping[str, Any]]) -> List[str]:
67
+ fields: List[str] = []
68
+ for example in dataset:
69
+ for key in example:
70
+ if not key.startswith("__") and key not in fields:
71
+ fields.append(key)
72
+ return fields
73
+
74
+
75
+ class _SuiteAssertionMapper:
76
+ """Maps ``(generated_output, case)`` into the assertion evaluator's input."""
77
+
78
+ def map(
79
+ self, generated_output: str, ground_truth_example: Mapping[str, Any]
80
+ ) -> Dict[str, Any]:
81
+ return {
82
+ "response": generated_output,
83
+ "__assertions__": list(ground_truth_example.get("__assertions__", [])),
84
+ "__vars__": {
85
+ key: value
86
+ for key, value in ground_truth_example.items()
87
+ if not key.startswith("__")
88
+ },
89
+ }
90
+
91
+
92
+ class _SuiteAssertionEvaluator:
93
+ """Scores generated outputs against the suite's own assertions."""
94
+
95
+ def __init__(self, evaluate_assertions: Any) -> None:
96
+ self._evaluate_assertions = evaluate_assertions
97
+
98
+ def evaluate(
99
+ self, inputs: Sequence[Mapping[str, Any]]
100
+ ) -> List[EvaluationResult]:
101
+ results: List[EvaluationResult] = []
102
+ for item in inputs:
103
+ summary = self._evaluate_assertions(
104
+ str(item.get("response", "")),
105
+ item.get("__assertions__", []),
106
+ variables=item.get("__vars__", {}),
107
+ )
108
+ score = float(summary.get("score", 0.0) or 0.0)
109
+ failed = [
110
+ res for res in summary.get("results", []) if not res.get("passed")
111
+ ]
112
+ if not failed:
113
+ reason = "all assertions passed"
114
+ else:
115
+ reason = "; ".join(
116
+ str(res.get("reason") or res.get("type")) for res in failed[:3]
117
+ )
118
+ results.append(EvaluationResult(score=score, reason=reason))
119
+ return results
120
+
121
+
122
+ def _resolve_model(
123
+ suite: Mapping[str, Any], optimizer_config: Mapping[str, Any]
124
+ ) -> str:
125
+ for key in ("model", "generator_model", "reflection_model", "teacher_model"):
126
+ value = optimizer_config.get(key)
127
+ if value:
128
+ return str(value)
129
+ for provider in suite.get("providers") or []:
130
+ if not isinstance(provider, Mapping):
131
+ continue
132
+ model = provider.get("model")
133
+ if not model:
134
+ continue
135
+ model = str(model)
136
+ provider_type = str(provider.get("type") or "").strip().lower()
137
+ if provider_type in {"vertex", "vertex_ai", "gemini"} and "/" not in model:
138
+ return f"vertex_ai/{model}"
139
+ return model
140
+ raise _manifest_error(
141
+ "generative optimization requires a task model: set "
142
+ "optimization.optimizer.model or add a provider with a `model`."
143
+ )
144
+
145
+
146
+ def _build_dataset(suite: Mapping[str, Any]) -> List[Dict[str, Any]]:
147
+ dataset: List[Dict[str, Any]] = []
148
+ for test in suite.get("tests") or []:
149
+ if not isinstance(test, Mapping):
150
+ continue
151
+ example = dict(test.get("vars") or test.get("variables") or {})
152
+ example["__assertions__"] = list(
153
+ test.get("assertions") or test.get("assert") or []
154
+ )
155
+ dataset.append(example)
156
+ return dataset
157
+
158
+
159
+ def _build_seed(suite: Mapping[str, Any]) -> str:
160
+ for prompt in suite.get("prompts") or []:
161
+ if isinstance(prompt, Mapping) and prompt.get("template"):
162
+ return _to_format_template(str(prompt["template"]))
163
+ if isinstance(prompt, str) and prompt.strip():
164
+ return _to_format_template(prompt)
165
+ raise _manifest_error(
166
+ "generative optimization requires at least one prompt template."
167
+ )
168
+
169
+
170
+ def _validate_seed(seed: str, example: Mapping[str, Any]) -> None:
171
+ variables = {k: v for k, v in example.items() if not k.startswith("__")}
172
+ try:
173
+ seed.format(**variables)
174
+ except (KeyError, IndexError, ValueError) as exc:
175
+ raise _manifest_error(
176
+ "generative seed prompt has placeholders the test vars don't cover "
177
+ f"or literal braces str.format can't handle ({exc}). Use `{{var}}` "
178
+ "placeholders that match test `vars` keys."
179
+ ) from exc
180
+
181
+
182
+ def _budget(optimizer_config: Mapping[str, Any], default: int) -> int:
183
+ for key in ("eval_budget", "max_metric_calls", "max_candidates"):
184
+ value = optimizer_config.get(key)
185
+ if value:
186
+ return int(value)
187
+ return default
188
+
189
+
190
+ def _run_optimizer(
191
+ token: str,
192
+ *,
193
+ model: str,
194
+ seed: str,
195
+ dataset: List[Dict[str, Any]],
196
+ evaluator: _SuiteAssertionEvaluator,
197
+ data_mapper: _SuiteAssertionMapper,
198
+ optimizer_config: Mapping[str, Any],
199
+ task_description: str,
200
+ ) -> Any:
201
+ from ..generators.litellm import LiteLLMGenerator
202
+
203
+ common = dict(evaluator=evaluator, data_mapper=data_mapper, dataset=dataset)
204
+ subset = len(dataset) or 1
205
+
206
+ try:
207
+ if token == "gepa":
208
+ from ..optimizers.gepa import GEPAOptimizer
209
+
210
+ optimizer = GEPAOptimizer(reflection_model=model, generator_model=model)
211
+ return optimizer.optimize(
212
+ **common,
213
+ initial_prompts=[seed],
214
+ max_metric_calls=_budget(optimizer_config, 25),
215
+ )
216
+ if token == "protegi":
217
+ from ..optimizers.protegi import ProTeGi
218
+
219
+ optimizer = ProTeGi(
220
+ teacher_generator=LiteLLMGenerator(model, "{prompt}"),
221
+ task_model=model,
222
+ num_gradients=int(optimizer_config.get("num_gradients", 2)),
223
+ beam_size=int(optimizer_config.get("beam_size", 2)),
224
+ )
225
+ return optimizer.optimize(
226
+ **common,
227
+ initial_prompts=[seed],
228
+ num_rounds=int(optimizer_config.get("num_rounds", 2)),
229
+ eval_subset_size=subset,
230
+ )
231
+ if token == "metaprompt":
232
+ from ..optimizers.metaprompt import MetaPromptOptimizer
233
+
234
+ optimizer = MetaPromptOptimizer(
235
+ teacher_generator=LiteLLMGenerator(model, "{prompt}"),
236
+ task_model=model,
237
+ )
238
+ return optimizer.optimize(
239
+ **common,
240
+ initial_prompts=[seed],
241
+ task_description=task_description,
242
+ num_rounds=int(optimizer_config.get("num_rounds", 3)),
243
+ eval_subset_size=subset,
244
+ )
245
+ if token == "promptwizard":
246
+ from ..optimizers.promptwizard import PromptWizardOptimizer
247
+
248
+ optimizer = PromptWizardOptimizer(
249
+ teacher_generator=LiteLLMGenerator(model, "{prompt}"),
250
+ task_model=model,
251
+ mutate_rounds=int(optimizer_config.get("mutate_rounds", 2)),
252
+ refine_iterations=int(optimizer_config.get("refine_iterations", 1)),
253
+ )
254
+ return optimizer.optimize(
255
+ **common,
256
+ initial_prompts=[seed],
257
+ task_description=task_description,
258
+ )
259
+ if token == "random_search":
260
+ from ..optimizers.random_search import RandomSearchOptimizer
261
+
262
+ optimizer = RandomSearchOptimizer(
263
+ generator=LiteLLMGenerator(model, seed),
264
+ teacher_model=model,
265
+ num_variations=int(optimizer_config.get("num_variations", 4)),
266
+ )
267
+ return optimizer.optimize(**common)
268
+ if token == "bayesian_search":
269
+ from ..optimizers.bayesian_search import BayesianSearchOptimizer
270
+
271
+ optimizer = BayesianSearchOptimizer(
272
+ inference_model_name=model,
273
+ n_trials=int(optimizer_config.get("n_trials", 6)),
274
+ example_template_fields=_dataset_fields(dataset) or None,
275
+ )
276
+ return optimizer.optimize(**common, initial_prompts=[seed])
277
+ except ImportError as exc:
278
+ raise _manifest_error(
279
+ f"the `{token}` optimizer needs an optional dependency: {exc}"
280
+ ) from exc
281
+
282
+ raise _manifest_error(
283
+ f"unknown generative optimizer token: {token!r}; expected one of "
284
+ f"{sorted(GENERATIVE_TOKENS)}"
285
+ )
286
+
287
+
288
+ def _build_payload(
289
+ result: Any,
290
+ *,
291
+ token: str,
292
+ model: str,
293
+ seed: str,
294
+ suite: Mapping[str, Any],
295
+ suite_path: str | Path,
296
+ threshold: float,
297
+ started: float,
298
+ ) -> Dict[str, Any]:
299
+ from fi.simulate.suite import (
300
+ CLI_SCHEMA_VERSION,
301
+ EVAL_SUITE_OPTIMIZATION_SCHEMA_VERSION,
302
+ )
303
+
304
+ try:
305
+ best_prompt = result.best_generator.get_prompt_template()
306
+ except Exception: # pragma: no cover - defensive
307
+ best_prompt = seed
308
+ final_score = float(getattr(result, "final_score", 0.0) or 0.0)
309
+ history = [
310
+ {
311
+ "prompt": history_item.prompt,
312
+ "average_score": round(float(history_item.average_score), 4),
313
+ }
314
+ for history_item in getattr(result, "history", []) or []
315
+ ]
316
+ status = "passed" if final_score >= threshold else "failed"
317
+ return {
318
+ "schema_version": CLI_SCHEMA_VERSION,
319
+ "kind": EVAL_SUITE_OPTIMIZATION_SCHEMA_VERSION,
320
+ "name": str(suite.get("name") or Path(suite_path).stem),
321
+ "status": status,
322
+ "exit_code": 0 if status == "passed" else 1,
323
+ "summary": {
324
+ "optimizer_algorithm": token,
325
+ "optimizer_family": "generative",
326
+ "final_score": round(final_score, 4),
327
+ "threshold": threshold,
328
+ "iterations": len(history),
329
+ },
330
+ "optimization": {
331
+ "source": "eval_suite",
332
+ "family": "generative",
333
+ "optimizer": token,
334
+ "model": model,
335
+ "threshold": threshold,
336
+ "final_score": round(final_score, 4),
337
+ "seed_prompt": seed,
338
+ "best_prompt": best_prompt,
339
+ "history": history,
340
+ "early_stopped": bool(getattr(result, "early_stopped", False)),
341
+ "stop_reason": getattr(result, "stop_reason", None),
342
+ "total_evaluations": getattr(result, "total_evaluations", None),
343
+ },
344
+ "duration_seconds": round(time.time() - started, 4),
345
+ }
346
+
347
+
348
+ def optimize_eval_suite_generative(
349
+ suite: Mapping[str, Any],
350
+ *,
351
+ token: str,
352
+ suite_path: str | Path = ".",
353
+ name: Optional[str] = None,
354
+ optimizer_config: Optional[Mapping[str, Any]] = None,
355
+ threshold: float = 0.5,
356
+ started: Optional[float] = None,
357
+ ) -> Dict[str, Any]:
358
+ """Optimize an eval suite with a generative (LLM prompt-rewriting) optimizer.
359
+
360
+ Builds a task-LLM generator + an assertion-scoring evaluator from the
361
+ suite, runs the requested optimizer, and returns an eval-suite optimization
362
+ payload with the seed and best prompts, score, and iteration history.
363
+ """
364
+ if token not in GENERATIVE_TOKENS:
365
+ raise _manifest_error(
366
+ f"unknown generative optimizer token: {token!r}; expected one of "
367
+ f"{sorted(GENERATIVE_TOKENS)}"
368
+ )
369
+ started = time.time() if started is None else started
370
+ optimizer_config = dict(optimizer_config or {})
371
+
372
+ evaluate_assertions = _suite_attr("evaluate_assertions")
373
+ model = _resolve_model(suite, optimizer_config)
374
+ dataset = _build_dataset(suite)
375
+ if not dataset:
376
+ raise _manifest_error(
377
+ "generative optimization requires at least one test case with vars."
378
+ )
379
+ seed = _build_seed(suite)
380
+ _validate_seed(seed, dataset[0])
381
+
382
+ evaluator = _SuiteAssertionEvaluator(evaluate_assertions)
383
+ data_mapper = _SuiteAssertionMapper()
384
+ suite_label = name or suite.get("name") or "the eval suite"
385
+ task_description = str(
386
+ suite.get("description")
387
+ or f"Rewrite the assistant prompt so its responses satisfy the eval "
388
+ f"assertions for {suite_label}."
389
+ )
390
+
391
+ result = _run_optimizer(
392
+ token,
393
+ model=model,
394
+ seed=seed,
395
+ dataset=dataset,
396
+ evaluator=evaluator,
397
+ data_mapper=data_mapper,
398
+ optimizer_config=optimizer_config,
399
+ task_description=task_description,
400
+ )
401
+ return _build_payload(
402
+ result,
403
+ token=token,
404
+ model=model,
405
+ seed=seed,
406
+ suite=suite,
407
+ suite_path=suite_path,
408
+ threshold=threshold,
409
+ started=started,
410
+ )