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,85 @@
1
+ from .capabilities import CapabilitySet, EndpointCapabilities
2
+ from .events import CanonicalEvent, EventReliability
3
+ from .failures import FailureStage, SimulationFailure
4
+ from .ids import (
5
+ derive_artifact_id,
6
+ derive_event_id,
7
+ derive_test_case_id,
8
+ new_plan_id,
9
+ new_run_id,
10
+ )
11
+ from .plan import (
12
+ SIMULATION_PLAN_VERSION,
13
+ AdapterRef,
14
+ ArtifactPlan,
15
+ EvidencePlan,
16
+ SimulationPlan,
17
+ )
18
+ from .report import (
19
+ SIMULATION_REPORT_SCHEMA_VERSION,
20
+ SimulationReport,
21
+ SimulationTestCaseResult,
22
+ )
23
+ from .run import CleanupStatus, RunStatus, SimulationRun, TestCaseStatus
24
+ from .spec import (
25
+ SIMULATION_SPEC_SCHEMA_VERSION,
26
+ AdapterSpec,
27
+ AgentEndpointSpec,
28
+ ArtifactPolicy,
29
+ CleanupPolicy,
30
+ ConversationDirection,
31
+ EnvironmentSpec,
32
+ EvaluationRef,
33
+ EvidencePolicy,
34
+ ExecutionPolicy,
35
+ RetryPolicy,
36
+ RuntimeIsolation,
37
+ RuntimeRequirements,
38
+ SecretRef,
39
+ SimulationSpec,
40
+ SimulatorPolicySpec,
41
+ TimeoutPolicy,
42
+ )
43
+
44
+ __all__ = [
45
+ "SIMULATION_PLAN_VERSION",
46
+ "SIMULATION_REPORT_SCHEMA_VERSION",
47
+ "SIMULATION_SPEC_SCHEMA_VERSION",
48
+ "AdapterRef",
49
+ "AdapterSpec",
50
+ "AgentEndpointSpec",
51
+ "ArtifactPlan",
52
+ "ArtifactPolicy",
53
+ "CanonicalEvent",
54
+ "CapabilitySet",
55
+ "CleanupPolicy",
56
+ "CleanupStatus",
57
+ "ConversationDirection",
58
+ "EndpointCapabilities",
59
+ "EnvironmentSpec",
60
+ "EvaluationRef",
61
+ "EventReliability",
62
+ "EvidencePlan",
63
+ "EvidencePolicy",
64
+ "ExecutionPolicy",
65
+ "FailureStage",
66
+ "RetryPolicy",
67
+ "RunStatus",
68
+ "RuntimeIsolation",
69
+ "RuntimeRequirements",
70
+ "SecretRef",
71
+ "SimulationFailure",
72
+ "SimulationPlan",
73
+ "SimulationReport",
74
+ "SimulationRun",
75
+ "SimulationSpec",
76
+ "SimulationTestCaseResult",
77
+ "SimulatorPolicySpec",
78
+ "TestCaseStatus",
79
+ "TimeoutPolicy",
80
+ "derive_artifact_id",
81
+ "derive_event_id",
82
+ "derive_test_case_id",
83
+ "new_plan_id",
84
+ "new_run_id",
85
+ ]
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, Field, field_validator
4
+
5
+
6
+ class EndpointCapabilities(BaseModel):
7
+ audio: bool = False
8
+ text: bool = False
9
+ streaming: bool = False
10
+ interruption: bool = False
11
+ dtmf: bool = False
12
+ transfer: bool = False
13
+ transcript_events: bool = False
14
+ tool_events: bool = False
15
+ usage_events: bool = False
16
+ internal_metrics: bool = False
17
+ recording: bool = False
18
+ web_rtc: bool = False
19
+ sip: bool = False
20
+
21
+ def supported(self) -> set[str]:
22
+ return {
23
+ name
24
+ for name, enabled in self.model_dump().items()
25
+ if enabled
26
+ }
27
+
28
+
29
+ class CapabilitySet(BaseModel):
30
+ required: list[str] = Field(default_factory=list)
31
+ supported: list[str] = Field(default_factory=list)
32
+ degraded: list[str] = Field(default_factory=list)
33
+
34
+ @field_validator("required", "supported", "degraded")
35
+ @classmethod
36
+ def _normalize(cls, values: list[str]) -> list[str]:
37
+ return sorted(set(values))
38
+
39
+ def missing(self) -> list[str]:
40
+ return sorted(set(self.required) - set(self.supported))
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from datetime import datetime, timezone
5
+ from enum import Enum
6
+
7
+ from pydantic import BaseModel, Field, JsonValue
8
+
9
+ from .ids import derive_event_id
10
+
11
+ EVENT_SCHEMA_VERSION = "futureagi.simulation-event.v1"
12
+
13
+
14
+ class EventReliability(str, Enum):
15
+ RELIABLE = "reliable"
16
+ BEST_EFFORT = "best_effort"
17
+ MEDIA = "media"
18
+
19
+
20
+ class CanonicalEvent(BaseModel):
21
+ schema_version: str = EVENT_SCHEMA_VERSION
22
+ event_id: str
23
+ run_id: str
24
+ test_case_id: str
25
+ session_id: str | None = None
26
+ type: str
27
+ source: str
28
+ provider: str | None = None
29
+ wall_time: datetime
30
+ monotonic_ns: int
31
+ reliability: EventReliability = EventReliability.RELIABLE
32
+ payload: dict[str, JsonValue] = Field(default_factory=dict)
33
+ trace_id: str | None = None
34
+ correlation_id: str | None = None
35
+ provider_raw_ref: str | None = None
36
+
37
+ @classmethod
38
+ def create(
39
+ cls,
40
+ *,
41
+ run_id: str,
42
+ test_case_id: str,
43
+ event_type: str,
44
+ source: str,
45
+ sequence: int,
46
+ provider: str | None = None,
47
+ session_id: str | None = None,
48
+ reliability: EventReliability = EventReliability.RELIABLE,
49
+ payload: dict[str, JsonValue] | None = None,
50
+ ) -> "CanonicalEvent":
51
+ return cls(
52
+ event_id=derive_event_id(test_case_id, source, sequence),
53
+ run_id=run_id,
54
+ test_case_id=test_case_id,
55
+ session_id=session_id,
56
+ type=event_type,
57
+ source=source,
58
+ provider=provider,
59
+ wall_time=datetime.now(timezone.utc),
60
+ monotonic_ns=time.monotonic_ns(),
61
+ reliability=reliability,
62
+ payload=payload or {},
63
+ )
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+ from pydantic import BaseModel, Field, JsonValue
6
+
7
+
8
+ class FailureStage(str, Enum):
9
+ PLANNING = "planning"
10
+ PREPARING = "preparing"
11
+ READINESS = "readiness"
12
+ RUNNING = "running"
13
+ FINALIZING = "finalizing"
14
+ SUBMITTING = "submitting"
15
+ CLEANUP = "cleanup"
16
+
17
+
18
+ class SimulationFailure(BaseModel):
19
+ stage: FailureStage
20
+ code: str
21
+ message: str
22
+ retryable: bool = False
23
+ provider: str | None = None
24
+ external_ref: str | None = None
25
+ details: dict[str, JsonValue] = Field(default_factory=dict)
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ from uuid import UUID, uuid4, uuid5
4
+
5
+ _ID_NAMESPACE = UUID("79626429-64cd-4ec6-88e8-97b61bb649f3")
6
+
7
+
8
+ def new_run_id() -> str:
9
+ return f"run_{uuid4().hex}"
10
+
11
+
12
+ def new_plan_id() -> str:
13
+ return f"plan_{uuid4().hex}"
14
+
15
+
16
+ def derive_test_case_id(run_id: str, persona_ref: str, index: int) -> str:
17
+ return _stable_id("case", run_id, persona_ref, str(index))
18
+
19
+
20
+ def derive_event_id(test_case_id: str, source: str, sequence: int) -> str:
21
+ return _stable_id("event", test_case_id, source, str(sequence))
22
+
23
+
24
+ def derive_artifact_id(
25
+ test_case_id: str,
26
+ logical_name: str,
27
+ checksum: str | None = None,
28
+ ) -> str:
29
+ return _stable_id("artifact", test_case_id, logical_name, checksum or "pending")
30
+
31
+
32
+ def _stable_id(prefix: str, *parts: str) -> str:
33
+ value = "\x1f".join((prefix, *parts))
34
+ return f"{prefix}_{uuid5(_ID_NAMESPACE, value).hex}"
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field, JsonValue, model_validator
4
+
5
+ from .capabilities import CapabilitySet
6
+ from fi.simulate._hashing import content_hash
7
+ from .spec import CleanupPolicy, RetryPolicy, RuntimeRequirements, TimeoutPolicy
8
+
9
+ SIMULATION_PLAN_VERSION = "futureagi.simulation-plan.v1"
10
+
11
+
12
+ class AdapterRef(BaseModel):
13
+ name: str
14
+ version: str
15
+ config: dict[str, JsonValue] = Field(default_factory=dict)
16
+
17
+
18
+ class EvidencePlan(BaseModel):
19
+ source_ids: list[str] = Field(default_factory=list)
20
+ required_capabilities: list[str] = Field(default_factory=list)
21
+
22
+
23
+ class ArtifactPlan(BaseModel):
24
+ enabled: bool = True
25
+ root_directory: str
26
+ record_audio: bool = False
27
+ required_types: list[str] = Field(default_factory=list)
28
+
29
+
30
+ class SimulationPlan(BaseModel):
31
+ model_config = ConfigDict(frozen=True)
32
+
33
+ plan_version: str = SIMULATION_PLAN_VERSION
34
+ plan_id: str
35
+ run_id: str
36
+ spec_hash: str
37
+ environment_adapter: AdapterRef
38
+ target_adapter: AdapterRef
39
+ simulator_adapter: AdapterRef
40
+ transport_adapter: AdapterRef | None = None
41
+ negotiated_capabilities: CapabilitySet = Field(default_factory=CapabilitySet)
42
+ runtime_requirements: RuntimeRequirements = Field(default_factory=RuntimeRequirements)
43
+ timeout_policy: TimeoutPolicy = Field(default_factory=TimeoutPolicy)
44
+ retry_policy: RetryPolicy = Field(default_factory=RetryPolicy)
45
+ cleanup_policy: CleanupPolicy = Field(default_factory=CleanupPolicy)
46
+ evidence_plan: EvidencePlan = Field(default_factory=EvidencePlan)
47
+ artifact_plan: ArtifactPlan
48
+ plan_hash: str | None = None
49
+
50
+ def content_hash(self) -> str:
51
+ return content_hash(
52
+ self.model_dump(exclude={"plan_hash"}, exclude_none=True)
53
+ )
54
+
55
+ @model_validator(mode="after")
56
+ def _validate_and_stamp(self) -> "SimulationPlan":
57
+ if self.plan_version != SIMULATION_PLAN_VERSION:
58
+ raise ValueError(
59
+ f"simulation_plan_version_unsupported: {self.plan_version}"
60
+ )
61
+ missing = self.negotiated_capabilities.missing()
62
+ if missing:
63
+ raise ValueError(
64
+ "simulation_capabilities_missing: " + ", ".join(missing)
65
+ )
66
+ expected = self.content_hash()
67
+ if self.plan_hash is not None and self.plan_hash != expected:
68
+ raise ValueError("simulation_plan_hash_mismatch")
69
+ object.__setattr__(self, "plan_hash", expected)
70
+ return self
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from fi.simulate.runtime.capabilities import CapabilitySet
4
+ from fi.simulate.runtime.ids import new_plan_id
5
+ from fi.simulate.runtime.plan import (
6
+ AdapterRef,
7
+ ArtifactPlan,
8
+ EvidencePlan,
9
+ SimulationPlan,
10
+ )
11
+ from fi.simulate.runtime.spec import SimulationSpec
12
+
13
+ # Import for the registration side effect: builtin endpoint profiles populate
14
+ # the endpoint_registry that build_plan reads capabilities from, and builtin
15
+ # simulator descriptors populate the simulator_registry that build_plan validates.
16
+ from fi.simulate.endpoints import profiles as _endpoint_profiles # noqa: F401
17
+ from fi.simulate.simulator import builtins as _simulator_builtins # noqa: F401
18
+ from fi.simulate.registry import (
19
+ AdapterNotFound,
20
+ endpoint_registry,
21
+ environment_registry,
22
+ simulator_registry,
23
+ )
24
+
25
+
26
+ class UnsupportedWorldKind(ValueError):
27
+ """Raised when a spec's ``world_kind`` isn't one the environment declares."""
28
+
29
+ def __init__(self, adapter: str, world_kind: str, supported: list[str]) -> None:
30
+ self.adapter = adapter
31
+ self.world_kind = world_kind
32
+ self.supported = sorted(supported)
33
+ super().__init__(
34
+ f"world_kind_unsupported: {world_kind!r} is not supported by "
35
+ f"environment {adapter!r}; supported: {self.supported}"
36
+ )
37
+
38
+
39
+ def build_plan(spec: SimulationSpec) -> SimulationPlan:
40
+ # Validate the simulator adapter against the registry (typo → clear error).
41
+ # Only enforce when the registry is populated with named builtins, so a
42
+ # third-party simulator registered by an integrator still passes.
43
+ if simulator_registry.get_or_none(spec.simulator.adapter) is None:
44
+ known = simulator_registry.names()
45
+ if known:
46
+ raise AdapterNotFound("simulator", spec.simulator.adapter, known)
47
+ # Validate world_kind against what the environment plugin declares. Empty
48
+ # declaration = unrestricted (third-party plugins that don't declare stay
49
+ # unbroken); a declared, non-empty list is enforced (typo → clear error).
50
+ env_factory = environment_registry.get_or_none(spec.environment.adapter)
51
+ supported_world_kinds = list(
52
+ getattr(getattr(env_factory, "manifest", None), "world_kinds", []) or []
53
+ )
54
+ if supported_world_kinds and spec.environment.world_kind not in supported_world_kinds:
55
+ raise UnsupportedWorldKind(
56
+ spec.environment.adapter,
57
+ spec.environment.world_kind,
58
+ supported_world_kinds,
59
+ )
60
+ profile = endpoint_registry.get_or_none(spec.target.adapter)
61
+ supported = (
62
+ sorted(profile.manifest.capabilities.supported()) if profile else []
63
+ )
64
+ root_directory = spec.artifacts.root_directory or f".fagi/runs/{spec.run_id}"
65
+ return SimulationPlan(
66
+ plan_id=new_plan_id(),
67
+ run_id=spec.run_id,
68
+ spec_hash=spec.spec_hash or spec.content_hash(),
69
+ environment_adapter=AdapterRef(
70
+ name=spec.environment.adapter,
71
+ version=spec.environment.adapter_version,
72
+ config=spec.environment.config,
73
+ ),
74
+ target_adapter=AdapterRef(
75
+ name=spec.target.adapter,
76
+ version=spec.target.adapter_version,
77
+ config=spec.target.config,
78
+ ),
79
+ simulator_adapter=AdapterRef(
80
+ name=spec.simulator.adapter,
81
+ version=spec.simulator.adapter_version,
82
+ config=spec.simulator.config,
83
+ ),
84
+ negotiated_capabilities=CapabilitySet(
85
+ required=spec.target.required_capabilities,
86
+ supported=supported,
87
+ ),
88
+ runtime_requirements=spec.execution.runtime,
89
+ timeout_policy=spec.execution.timeout,
90
+ retry_policy=spec.execution.retry,
91
+ cleanup_policy=spec.execution.cleanup,
92
+ evidence_plan=EvidencePlan(
93
+ source_ids=[source.source_id for source in spec.evidence.sources],
94
+ required_capabilities=spec.evidence.required_capabilities,
95
+ ),
96
+ artifact_plan=ArtifactPlan(
97
+ enabled=spec.artifacts.enabled,
98
+ root_directory=root_directory,
99
+ record_audio=spec.artifacts.record_audio,
100
+ required_types=spec.artifacts.required_types,
101
+ ),
102
+ )
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from datetime import datetime, timezone
5
+
6
+ from pydantic import BaseModel, Field, JsonValue, model_validator
7
+
8
+ from fi.simulate.artifacts import ArtifactManifest
9
+ from fi.simulate.evidence import EvidenceSourceSummary
10
+ from fi.simulate.simulation.models import Persona, TestCaseResult, TestReport
11
+
12
+ from .failures import SimulationFailure
13
+ from fi.simulate._hashing import content_hash
14
+ from .ids import derive_test_case_id
15
+ from .run import CleanupStatus, RunStatus, TestCaseStatus
16
+
17
+ SIMULATION_REPORT_SCHEMA_VERSION = "futureagi.simulation-report.v1"
18
+
19
+
20
+ class SimulationTestCaseResult(BaseModel):
21
+ test_case_id: str
22
+ status: TestCaseStatus
23
+ persona: Persona
24
+ result: TestCaseResult | None = None
25
+ failure: SimulationFailure | None = None
26
+ evidence: list[EvidenceSourceSummary] = Field(default_factory=list)
27
+ artifact_ids: list[str] = Field(default_factory=list)
28
+ started_at: datetime | None = None
29
+ ended_at: datetime | None = None
30
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
31
+
32
+ @classmethod
33
+ def from_legacy_case(
34
+ cls,
35
+ result: TestCaseResult,
36
+ *,
37
+ index: int,
38
+ run_id: str,
39
+ evidence: list[EvidenceSourceSummary] | None = None,
40
+ ) -> "SimulationTestCaseResult":
41
+ """Convert one legacy ``TestCaseResult`` into its canonical form.
42
+
43
+ Shared by ``SimulationReport.from_legacy`` (report-level) and the hosted
44
+ runner's per-case streaming callback, so a case streamed mid-run is
45
+ byte-identical to the one the finalized report carries.
46
+ """
47
+ persona_ref = result.persona.version or result.persona.content_hash()
48
+ test_case_id = str(
49
+ result.metadata.get("test_case_id")
50
+ or derive_test_case_id(run_id, persona_ref, index)
51
+ )
52
+ case_status = TestCaseStatus(
53
+ result.metadata.get("status", TestCaseStatus.COMPLETED.value)
54
+ )
55
+ raw_failure = result.metadata.get("failure")
56
+ failure = (
57
+ SimulationFailure.model_validate(raw_failure)
58
+ if isinstance(raw_failure, Mapping)
59
+ else None
60
+ )
61
+ return cls(
62
+ test_case_id=test_case_id,
63
+ status=case_status,
64
+ persona=result.persona,
65
+ result=result,
66
+ failure=failure,
67
+ evidence=[item.model_copy(deep=True) for item in evidence or []],
68
+ )
69
+
70
+ @model_validator(mode="after")
71
+ def _validate_outcome(self) -> "SimulationTestCaseResult":
72
+ if self.status == TestCaseStatus.COMPLETED and self.result is None:
73
+ raise ValueError("test_case_result_missing: completed case requires result")
74
+ failure_statuses = {
75
+ TestCaseStatus.FAILED,
76
+ TestCaseStatus.TIMED_OUT,
77
+ TestCaseStatus.AGENT_UNAVAILABLE,
78
+ }
79
+ if self.status in failure_statuses and self.failure is None:
80
+ raise ValueError("test_case_failure_missing: failed case requires failure")
81
+ return self
82
+
83
+
84
+ class SimulationReport(BaseModel):
85
+ schema_version: str = SIMULATION_REPORT_SCHEMA_VERSION
86
+ run_id: str
87
+ plan_id: str | None = None
88
+ spec_hash: str
89
+ status: RunStatus
90
+ cleanup_status: CleanupStatus = CleanupStatus.PENDING
91
+ started_at: datetime
92
+ ended_at: datetime | None = None
93
+ test_cases: list[SimulationTestCaseResult] = Field(default_factory=list)
94
+ artifacts: ArtifactManifest
95
+ failure: SimulationFailure | None = None
96
+ cleanup_failure: SimulationFailure | None = None
97
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)
98
+ report_hash: str | None = None
99
+
100
+ def content_hash(self) -> str:
101
+ payload = self.model_dump(exclude={"report_hash"}, exclude_none=True)
102
+ payload["test_cases"] = sorted(
103
+ payload["test_cases"], key=lambda item: item["test_case_id"]
104
+ )
105
+ return content_hash(payload)
106
+
107
+ def to_legacy(self, *, include_runtime_metadata: bool = True) -> TestReport:
108
+ results = []
109
+ for case in self.test_cases:
110
+ if case.result is not None:
111
+ result = case.result.model_copy(deep=True)
112
+ else:
113
+ result = TestCaseResult(persona=case.persona, transcript="")
114
+ if include_runtime_metadata:
115
+ result.metadata.update(
116
+ {
117
+ "run_id": self.run_id,
118
+ "test_case_id": case.test_case_id,
119
+ "status": case.status.value,
120
+ }
121
+ )
122
+ if case.failure is not None:
123
+ result.metadata["failure"] = case.failure.model_dump(
124
+ mode="json", exclude_none=True
125
+ )
126
+ results.append(result)
127
+ return TestReport(results=results)
128
+
129
+ @classmethod
130
+ def from_legacy(
131
+ cls,
132
+ report: TestReport,
133
+ *,
134
+ run_id: str,
135
+ spec_hash: str,
136
+ status: RunStatus = RunStatus.COMPLETED,
137
+ started_at: datetime | None = None,
138
+ ended_at: datetime | None = None,
139
+ plan_id: str | None = None,
140
+ artifacts: ArtifactManifest | None = None,
141
+ evidence: list[EvidenceSourceSummary] | None = None,
142
+ ) -> "SimulationReport":
143
+ cases = [
144
+ SimulationTestCaseResult.from_legacy_case(
145
+ result, index=index, run_id=run_id, evidence=evidence
146
+ )
147
+ for index, result in enumerate(report.results)
148
+ ]
149
+ return cls(
150
+ run_id=run_id,
151
+ plan_id=plan_id,
152
+ spec_hash=spec_hash,
153
+ status=status,
154
+ started_at=started_at or datetime.now(timezone.utc),
155
+ ended_at=ended_at,
156
+ test_cases=cases,
157
+ artifacts=artifacts or ArtifactManifest(run_id=run_id),
158
+ )
159
+
160
+ @model_validator(mode="after")
161
+ def _validate_and_stamp(self) -> "SimulationReport":
162
+ if self.schema_version != SIMULATION_REPORT_SCHEMA_VERSION:
163
+ raise ValueError(
164
+ f"simulation_report_version_unsupported: {self.schema_version}"
165
+ )
166
+ if self.artifacts.run_id != self.run_id:
167
+ raise ValueError("simulation_report_artifact_run_mismatch")
168
+ if self.status == RunStatus.FAILED and self.failure is None:
169
+ raise ValueError("simulation_report_failure_missing")
170
+ expected = self.content_hash()
171
+ if self.report_hash is not None and self.report_hash != expected:
172
+ raise ValueError("simulation_report_hash_mismatch")
173
+ object.__setattr__(self, "report_hash", expected)
174
+ return self
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from enum import Enum
5
+
6
+ from pydantic import BaseModel, Field, JsonValue
7
+
8
+ from .failures import SimulationFailure
9
+
10
+
11
+ class RunStatus(str, Enum):
12
+ CREATED = "created"
13
+ PLANNING = "planning"
14
+ PREPARING = "preparing"
15
+ READY = "ready"
16
+ RUNNING = "running"
17
+ FINALIZING = "finalizing"
18
+ SUBMITTING = "submitting"
19
+ COMPLETED = "completed"
20
+ FAILED = "failed"
21
+ CANCELED = "canceled"
22
+ TIMED_OUT = "timed_out"
23
+
24
+ @property
25
+ def terminal(self) -> bool:
26
+ return self in {
27
+ RunStatus.COMPLETED,
28
+ RunStatus.FAILED,
29
+ RunStatus.CANCELED,
30
+ RunStatus.TIMED_OUT,
31
+ }
32
+
33
+
34
+ class TestCaseStatus(str, Enum):
35
+ CREATED = "created"
36
+ PREPARING = "preparing"
37
+ READY = "ready"
38
+ RUNNING = "running"
39
+ COMPLETED = "completed"
40
+ FAILED = "failed"
41
+ CANCELED = "canceled"
42
+ TIMED_OUT = "timed_out"
43
+ AGENT_UNAVAILABLE = "agent_unavailable"
44
+ UNSUPPORTED = "unsupported"
45
+ INCONCLUSIVE = "inconclusive"
46
+
47
+ @property
48
+ def terminal(self) -> bool:
49
+ return self not in {
50
+ TestCaseStatus.CREATED,
51
+ TestCaseStatus.PREPARING,
52
+ TestCaseStatus.READY,
53
+ TestCaseStatus.RUNNING,
54
+ }
55
+
56
+
57
+ class CleanupStatus(str, Enum):
58
+ PENDING = "pending"
59
+ RUNNING = "running"
60
+ COMPLETED = "completed"
61
+ FAILED = "failed"
62
+
63
+
64
+ class SimulationRun(BaseModel):
65
+ run_id: str
66
+ plan_id: str | None = None
67
+ spec_hash: str
68
+ status: RunStatus = RunStatus.CREATED
69
+ cleanup_status: CleanupStatus = CleanupStatus.PENDING
70
+ created_at: datetime
71
+ started_at: datetime | None = None
72
+ ended_at: datetime | None = None
73
+ failure: SimulationFailure | None = None
74
+ cleanup_failure: SimulationFailure | None = None
75
+ metadata: dict[str, JsonValue] = Field(default_factory=dict)