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,165 @@
1
+ """Config command for managing CLI configuration."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ import typer
8
+
9
+ from fi.cli.config.defaults import BASIC_TEMPLATE
10
+ from fi.cli.utils.console import console, print_success, print_warning
11
+
12
+
13
+ config_app = typer.Typer(
14
+ name="config",
15
+ help="Manage CLI configuration",
16
+ no_args_is_help=True,
17
+ )
18
+
19
+
20
+ @config_app.command("show")
21
+ def show_config() -> None:
22
+ """Display current configuration settings."""
23
+ config_items = {
24
+ "FI_API_KEY": _mask_key(os.environ.get("FI_API_KEY")),
25
+ "FI_SECRET_KEY": _mask_key(os.environ.get("FI_SECRET_KEY")),
26
+ "FI_BASE_URL": os.environ.get("FI_BASE_URL", "https://api.futureagi.com"),
27
+ "FI_DEFAULT_MODEL": os.environ.get("FI_DEFAULT_MODEL", "gpt-4o"),
28
+ }
29
+
30
+ console.print("[bold]Current Configuration:[/bold]\n")
31
+ for key, value in config_items.items():
32
+ if value:
33
+ console.print(f" [cyan]{key}[/cyan]: {value}")
34
+ else:
35
+ console.print(f" [cyan]{key}[/cyan]: [dim]not set[/dim]")
36
+
37
+
38
+ @config_app.command("get")
39
+ def get_config(
40
+ key: str = typer.Argument(..., help="Configuration key to get"),
41
+ ) -> None:
42
+ """Get a specific configuration value."""
43
+ value = os.environ.get(key)
44
+ if value:
45
+ # Mask sensitive keys
46
+ if "KEY" in key.upper() or "SECRET" in key.upper():
47
+ console.print(_mask_key(value))
48
+ else:
49
+ console.print(value)
50
+ else:
51
+ print_warning(f"Configuration key not set: {key}")
52
+ raise typer.Exit(1)
53
+
54
+
55
+ @config_app.command("set")
56
+ def set_config(
57
+ key: str = typer.Argument(..., help="Configuration key to set"),
58
+ value: str = typer.Argument(..., help="Value to set"),
59
+ save: bool = typer.Option(
60
+ False,
61
+ "--save", "-s",
62
+ help="Save to .env file in current directory",
63
+ ),
64
+ ) -> None:
65
+ """
66
+ Set a configuration value.
67
+
68
+ Note: Without --save, the value is only set for the current session.
69
+ Use --save to persist the value to a .env file.
70
+ """
71
+ os.environ[key] = value
72
+
73
+ if save:
74
+ _save_to_env_file(key, value)
75
+ print_success(f"Saved {key} to .env file")
76
+ else:
77
+ print_success(f"Set {key} for current session")
78
+ console.print("[dim]Use --save to persist to .env file[/dim]")
79
+
80
+
81
+ @config_app.command("init")
82
+ def init_config(
83
+ path: Path = typer.Argument(
84
+ Path("."),
85
+ help="Directory to create config file in",
86
+ ),
87
+ force: bool = typer.Option(
88
+ False,
89
+ "--force", "-f",
90
+ help="Overwrite existing config file",
91
+ ),
92
+ ) -> None:
93
+ """Create a default configuration file."""
94
+ config_path = path / "fi-evaluation.yaml"
95
+
96
+ if config_path.exists() and not force:
97
+ print_warning(
98
+ f"Configuration file already exists: {config_path}\n"
99
+ "Use --force to overwrite."
100
+ )
101
+ raise typer.Exit(1)
102
+
103
+ with open(config_path, "w") as f:
104
+ f.write(BASIC_TEMPLATE)
105
+
106
+ print_success(f"Created configuration file: {config_path}")
107
+
108
+
109
+ @config_app.command("env")
110
+ def show_env_template() -> None:
111
+ """Show environment variable template."""
112
+ template = """# Future AGI Evaluation Configuration
113
+ # Copy this to a .env file and fill in your values
114
+
115
+ # Required: API credentials
116
+ FI_API_KEY=your_api_key_here
117
+ FI_SECRET_KEY=your_secret_key_here
118
+
119
+ # Optional: Custom settings
120
+ # FI_BASE_URL=https://api.futureagi.com
121
+ # FI_DEFAULT_MODEL=gpt-4o
122
+
123
+ # Optional: Langfuse integration
124
+ # LANGFUSE_SECRET_KEY=your_langfuse_secret
125
+ # LANGFUSE_PUBLIC_KEY=your_langfuse_public
126
+ # LANGFUSE_HOST=https://cloud.langfuse.com
127
+ """
128
+ console.print(template)
129
+
130
+
131
+ def _mask_key(value: Optional[str]) -> Optional[str]:
132
+ """Mask a sensitive key value."""
133
+ if not value:
134
+ return None
135
+ if len(value) <= 8:
136
+ return "*" * len(value)
137
+ return value[:4] + "*" * (len(value) - 8) + value[-4:]
138
+
139
+
140
+ def _save_to_env_file(key: str, value: str) -> None:
141
+ """Save a key-value pair to .env file."""
142
+ env_path = Path(".env")
143
+
144
+ # Read existing content
145
+ existing_lines = []
146
+ if env_path.exists():
147
+ with open(env_path, "r") as f:
148
+ existing_lines = f.readlines()
149
+
150
+ # Update or add the key
151
+ key_found = False
152
+ new_lines = []
153
+ for line in existing_lines:
154
+ if line.startswith(f"{key}="):
155
+ new_lines.append(f"{key}={value}\n")
156
+ key_found = True
157
+ else:
158
+ new_lines.append(line)
159
+
160
+ if not key_found:
161
+ new_lines.append(f"{key}={value}\n")
162
+
163
+ # Write back
164
+ with open(env_path, "w") as f:
165
+ f.writelines(new_lines)
@@ -0,0 +1,208 @@
1
+ """Export command for exporting evaluation results."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ import typer
8
+
9
+ from fi.cli.storage import RunHistory
10
+ from fi.cli.utils.console import print_error, print_success, print_warning
11
+
12
+
13
+ def export(
14
+ run_id: Optional[str] = typer.Argument(
15
+ None,
16
+ help="Run ID to export (use 'fi view --list' to see available runs)",
17
+ ),
18
+ output: Path = typer.Option(
19
+ ...,
20
+ "--output", "-o",
21
+ help="Output file path",
22
+ ),
23
+ format: str = typer.Option(
24
+ "json",
25
+ "--format", "-f",
26
+ help="Export format: json, csv, html, junit",
27
+ ),
28
+ last: bool = typer.Option(
29
+ False,
30
+ "--last", "-l",
31
+ help="Export the most recent run",
32
+ ),
33
+ ) -> None:
34
+ """
35
+ Export evaluation results to a file.
36
+
37
+ Supported formats:
38
+ - json: JSON format with full details
39
+ - csv: CSV spreadsheet format
40
+ - html: HTML report for viewing in browser
41
+ - junit: JUnit XML format for CI/CD integration
42
+
43
+ Examples:
44
+ fi export --last -o results.json
45
+ fi export --last -o report.html -f html
46
+ fi export --last -o results.xml -f junit
47
+ fi export 20260123-143022-abc -o export.csv -f csv
48
+ """
49
+ history = RunHistory()
50
+
51
+ # Get the run to export
52
+ if last:
53
+ record = history.get_latest_run()
54
+ if record is None:
55
+ print_warning("No runs found. Run 'fi run' first to create evaluations.")
56
+ raise typer.Exit(1)
57
+ elif run_id:
58
+ record = history.get_run(run_id)
59
+ if record is None:
60
+ print_error(f"Run not found: {run_id}\nUse 'fi view --list' to see available runs.")
61
+ raise typer.Exit(1)
62
+ else:
63
+ print_error("Please specify a run ID or use --last to export the most recent run.")
64
+ raise typer.Exit(1)
65
+
66
+ # Load full results
67
+ results = history.load_results(record.run_id)
68
+ if results is None:
69
+ print_error(f"Results file not found for run: {record.run_id}")
70
+ raise typer.Exit(1)
71
+
72
+ # Export based on format
73
+ format = format.lower()
74
+ if format == "json":
75
+ _export_json(record, results, output)
76
+ elif format == "csv":
77
+ _export_csv(record, results, output)
78
+ elif format == "html":
79
+ _export_html(record, results, output)
80
+ elif format == "junit":
81
+ _export_junit(record, results, output)
82
+ else:
83
+ print_error(f"Unsupported format: {format}\nSupported: json, csv, html, junit")
84
+ raise typer.Exit(1)
85
+
86
+ print_success(f"Exported run {record.run_id} to {output}")
87
+
88
+
89
+ def _export_json(record, results: dict, output: Path) -> None:
90
+ """Export results as JSON."""
91
+ export_data = {
92
+ "run_id": record.run_id,
93
+ "timestamp": record.timestamp,
94
+ "config_file": record.config_file,
95
+ "templates": record.templates,
96
+ "summary": {
97
+ "total": record.total_evaluations,
98
+ "successful": record.successful,
99
+ "failed": record.failed,
100
+ "pass_rate": record.pass_rate,
101
+ "avg_score": record.avg_score,
102
+ },
103
+ "eval_results": results.get("eval_results", []),
104
+ }
105
+
106
+ with open(output, "w") as f:
107
+ json.dump(export_data, f, indent=2)
108
+
109
+
110
+ def _export_csv(record, results: dict, output: Path) -> None:
111
+ """Export results as CSV."""
112
+ import csv
113
+
114
+ with open(output, "w", newline="") as f:
115
+ writer = csv.writer(f)
116
+
117
+ # Header
118
+ writer.writerow([
119
+ "run_id",
120
+ "timestamp",
121
+ "template",
122
+ "output",
123
+ "reason",
124
+ "runtime",
125
+ "output_type",
126
+ "eval_id",
127
+ ])
128
+
129
+ # Data rows
130
+ for result in results.get("eval_results", []):
131
+ writer.writerow([
132
+ record.run_id,
133
+ record.timestamp,
134
+ result.get("name", ""),
135
+ result.get("output", ""),
136
+ result.get("reason", ""),
137
+ result.get("runtime", ""),
138
+ result.get("output_type", ""),
139
+ result.get("eval_id", ""),
140
+ ])
141
+
142
+
143
+ def _export_html(record, results: dict, output: Path) -> None:
144
+ """Export results as HTML."""
145
+ from fi.cli.commands.view import _generate_html_report
146
+
147
+ html_content = _generate_html_report(record, results)
148
+
149
+ with open(output, "w") as f:
150
+ f.write(html_content)
151
+
152
+
153
+ def _export_junit(record, results: dict, output: Path) -> None:
154
+ """Export results as JUnit XML for CI/CD integration."""
155
+ import xml.etree.ElementTree as ET
156
+ from xml.dom import minidom
157
+
158
+ # Create testsuites root
159
+ testsuites = ET.Element("testsuites")
160
+ testsuites.set("name", "AI Evaluation Results")
161
+ testsuites.set("tests", str(record.total_evaluations))
162
+ testsuites.set("failures", str(record.failed))
163
+ testsuites.set("time", "0")
164
+
165
+ # Create testsuite for this run
166
+ testsuite = ET.SubElement(testsuites, "testsuite")
167
+ testsuite.set("name", f"Run {record.run_id}")
168
+ testsuite.set("tests", str(record.total_evaluations))
169
+ testsuite.set("failures", str(record.failed))
170
+ testsuite.set("timestamp", record.timestamp)
171
+
172
+ # Add test cases
173
+ for result in results.get("eval_results", []):
174
+ testcase = ET.SubElement(testsuite, "testcase")
175
+ testcase.set("name", result.get("name", "Unknown"))
176
+ testcase.set("classname", "fi.evals")
177
+
178
+ runtime_ms = result.get("runtime", 0)
179
+ runtime_sec = runtime_ms / 1000 if runtime_ms else 0
180
+ testcase.set("time", f"{runtime_sec:.3f}")
181
+
182
+ result_output = result.get("output")
183
+ reason = result.get("reason", "")
184
+
185
+ # Check if this is a failure
186
+ is_failure = False
187
+ if isinstance(result_output, bool) and result_output is False:
188
+ is_failure = True
189
+ elif isinstance(result_output, (int, float)) and result_output < 0.5:
190
+ # Consider scores below 0.5 as failures for JUnit purposes
191
+ is_failure = True
192
+
193
+ if is_failure:
194
+ failure = ET.SubElement(testcase, "failure")
195
+ failure.set("message", f"Evaluation failed: {result_output}")
196
+ failure.text = reason
197
+
198
+ # Pretty print XML
199
+ xml_str = ET.tostring(testsuites, encoding="unicode")
200
+ dom = minidom.parseString(xml_str)
201
+ pretty_xml = dom.toprettyxml(indent=" ")
202
+
203
+ # Remove extra blank lines
204
+ lines = [line for line in pretty_xml.split("\n") if line.strip()]
205
+ pretty_xml = "\n".join(lines)
206
+
207
+ with open(output, "w") as f:
208
+ f.write(pretty_xml)
@@ -0,0 +1,112 @@
1
+ """Init command for creating new evaluation projects."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from fi.cli.config.defaults import TEMPLATES, SAMPLE_DATA
9
+ from fi.cli.utils.console import console, print_success, print_error, print_warning
10
+
11
+
12
+ def init_project(
13
+ directory: Optional[Path] = typer.Argument(
14
+ None,
15
+ help="Directory to initialize (default: current directory)",
16
+ ),
17
+ template: str = typer.Option(
18
+ "basic",
19
+ "--template", "-t",
20
+ help="Template to use: basic, rag, safety, agent",
21
+ ),
22
+ force: bool = typer.Option(
23
+ False,
24
+ "--force", "-f",
25
+ help="Overwrite existing configuration file",
26
+ ),
27
+ ) -> None:
28
+ """
29
+ Initialize a new AI evaluation project.
30
+
31
+ Creates a fi-evaluation.yaml configuration file and sample data files.
32
+ """
33
+ # Determine target directory
34
+ if directory is None:
35
+ target_dir = Path.cwd()
36
+ else:
37
+ target_dir = Path(directory).resolve()
38
+
39
+ # Validate template
40
+ if template not in TEMPLATES:
41
+ print_error(
42
+ f"Unknown template: {template}\n"
43
+ f"Available templates: {', '.join(TEMPLATES.keys())}"
44
+ )
45
+ raise typer.Exit(1)
46
+
47
+ # Create directory if it doesn't exist
48
+ if not target_dir.exists():
49
+ target_dir.mkdir(parents=True)
50
+ console.print(f"[dim]Created directory: {target_dir}[/dim]")
51
+
52
+ # Check for existing config
53
+ config_path = target_dir / "fi-evaluation.yaml"
54
+ if config_path.exists() and not force:
55
+ print_warning(
56
+ f"Configuration file already exists: {config_path}\n"
57
+ "Use --force to overwrite."
58
+ )
59
+ raise typer.Exit(1)
60
+
61
+ # Create data directory
62
+ data_dir = target_dir / "data"
63
+ data_dir.mkdir(exist_ok=True)
64
+
65
+ # Create results directory
66
+ results_dir = target_dir / "results"
67
+ results_dir.mkdir(exist_ok=True)
68
+
69
+ # Write configuration file
70
+ config_content = TEMPLATES[template]
71
+ with open(config_path, "w") as f:
72
+ f.write(config_content)
73
+ console.print(f"[dim]Created: {config_path}[/dim]")
74
+
75
+ # Write sample data
76
+ sample_data = SAMPLE_DATA.get(template, SAMPLE_DATA["basic"])
77
+ data_filename = _get_data_filename(template)
78
+ data_path = data_dir / data_filename
79
+ with open(data_path, "w") as f:
80
+ f.write(sample_data)
81
+ console.print(f"[dim]Created: {data_path}[/dim]")
82
+
83
+ # Create .gitignore for results
84
+ gitignore_path = results_dir / ".gitignore"
85
+ with open(gitignore_path, "w") as f:
86
+ f.write("# Ignore evaluation results\n*\n!.gitignore\n")
87
+
88
+ # Success message
89
+ print_success(
90
+ f"Initialized evaluation project with '{template}' template!\n\n"
91
+ f"Project structure:\n"
92
+ f" {target_dir}/\n"
93
+ f" ├── fi-evaluation.yaml # Configuration file\n"
94
+ f" ├── data/\n"
95
+ f" │ └── {data_filename} # Sample test data\n"
96
+ f" └── results/ # Evaluation results\n\n"
97
+ f"Next steps:\n"
98
+ f" 1. Edit fi-evaluation.yaml to configure your evaluations\n"
99
+ f" 2. Add your test data to the data/ directory\n"
100
+ f" 3. Run 'fi run' to execute evaluations"
101
+ )
102
+
103
+
104
+ def _get_data_filename(template: str) -> str:
105
+ """Get the appropriate data filename for the template."""
106
+ filenames = {
107
+ "basic": "test_cases.json",
108
+ "rag": "rag_test_cases.json",
109
+ "safety": "safety_test_cases.json",
110
+ "agent": "agent_test_cases.json",
111
+ }
112
+ return filenames.get(template, "test_cases.json")
@@ -0,0 +1,213 @@
1
+ """List command for displaying available resources."""
2
+
3
+ import json
4
+ from typing import Optional
5
+
6
+ import typer
7
+ from rich.table import Table
8
+
9
+ from fi.cli.utils.console import console, print_error
10
+ from fi.evals.templates import EvalTemplate
11
+
12
+
13
+ # Template categories for filtering
14
+ TEMPLATE_CATEGORIES = {
15
+ "conversation": [
16
+ "conversation_coherence",
17
+ "conversation_resolution",
18
+ ],
19
+ "rag": [
20
+ "context_adherence",
21
+ "context_relevance",
22
+ "completeness",
23
+ "chunk_attribution",
24
+ "chunk_utilization",
25
+ "groundedness",
26
+ ],
27
+ "safety": [
28
+ "content_moderation",
29
+ "pii",
30
+ "toxicity",
31
+ "prompt_injection",
32
+ "content_safety_violation",
33
+ "no_harmful_therapeutic_guidance",
34
+ "is_harmful_advice",
35
+ ],
36
+ "bias": [
37
+ "no_racial_bias",
38
+ "no_gender_bias",
39
+ "no_age_bias",
40
+ "bias_detection",
41
+ "sexist",
42
+ ],
43
+ "quality": [
44
+ "factual_accuracy",
45
+ "summary_quality",
46
+ "is_good_summary",
47
+ "is_factually_consistent",
48
+ "completeness",
49
+ ],
50
+ "format": [
51
+ "is_json",
52
+ "is_csv",
53
+ "is_code",
54
+ "is_email",
55
+ "one_line",
56
+ "contains_valid_link",
57
+ "no_valid_links",
58
+ ],
59
+ "tone": [
60
+ "tone",
61
+ "is_polite",
62
+ "is_concise",
63
+ "is_helpful",
64
+ "is_informal_tone",
65
+ "clinically_inappropriate_tone",
66
+ ],
67
+ "translation": [
68
+ "translation_accuracy",
69
+ "cultural_sensitivity",
70
+ ],
71
+ "audio": [
72
+ "audio_transcription",
73
+ "audio_quality",
74
+ ],
75
+ "function_calling": [
76
+ "llm_function_calling",
77
+ "evaluate_function_calling",
78
+ ],
79
+ "hallucination": [
80
+ "detect_hallucination_missing_info",
81
+ "caption_hallucination",
82
+ ],
83
+ }
84
+
85
+
86
+ def list_resources(
87
+ resource: str = typer.Argument(
88
+ "templates",
89
+ help="Resource to list: templates, categories",
90
+ ),
91
+ format: str = typer.Option(
92
+ "table",
93
+ "--format", "-f",
94
+ help="Output format: table, json",
95
+ ),
96
+ category: Optional[str] = typer.Option(
97
+ None,
98
+ "--category", "-c",
99
+ help="Filter templates by category",
100
+ ),
101
+ ) -> None:
102
+ """
103
+ List available evaluation resources.
104
+
105
+ Resources:
106
+ templates - List all available evaluation templates
107
+ categories - List template categories
108
+ """
109
+ if resource == "templates":
110
+ _list_templates(format, category)
111
+ elif resource == "categories":
112
+ _list_categories(format)
113
+ else:
114
+ print_error(f"Unknown resource: {resource}\nAvailable: templates, categories")
115
+ raise typer.Exit(1)
116
+
117
+
118
+ def _list_templates(format: str, category: Optional[str] = None) -> None:
119
+ """List all available evaluation templates."""
120
+ # Get all template classes from templates module
121
+ from fi.evals import templates as templates_module
122
+
123
+ template_list = []
124
+ for name in dir(templates_module):
125
+ obj = getattr(templates_module, name)
126
+ if (
127
+ isinstance(obj, type)
128
+ and issubclass(obj, EvalTemplate)
129
+ and obj is not EvalTemplate
130
+ and hasattr(obj, "eval_name")
131
+ ):
132
+ eval_name = obj.eval_name
133
+ # Filter by category if specified
134
+ if category:
135
+ if category not in TEMPLATE_CATEGORIES:
136
+ print_error(
137
+ f"Unknown category: {category}\n"
138
+ f"Available: {', '.join(TEMPLATE_CATEGORIES.keys())}"
139
+ )
140
+ raise typer.Exit(1)
141
+ if eval_name not in TEMPLATE_CATEGORIES[category]:
142
+ continue
143
+
144
+ # Determine category for template
145
+ template_category = _get_template_category(eval_name)
146
+
147
+ template_list.append({
148
+ "name": eval_name,
149
+ "class": name,
150
+ "category": template_category,
151
+ })
152
+
153
+ # Sort by name
154
+ template_list.sort(key=lambda x: x["name"])
155
+
156
+ if format == "json":
157
+ console.print(json.dumps(template_list, indent=2))
158
+ else:
159
+ _print_templates_table(template_list)
160
+
161
+
162
+ def _list_categories(format: str) -> None:
163
+ """List all template categories."""
164
+ categories = [
165
+ {"name": cat, "count": len(templates)}
166
+ for cat, templates in TEMPLATE_CATEGORIES.items()
167
+ ]
168
+
169
+ if format == "json":
170
+ console.print(json.dumps(categories, indent=2))
171
+ else:
172
+ table = Table(
173
+ title="Template Categories",
174
+ show_header=True,
175
+ header_style="bold cyan",
176
+ )
177
+ table.add_column("Category", style="green")
178
+ table.add_column("Templates", justify="right", style="blue")
179
+
180
+ for cat in categories:
181
+ table.add_row(cat["name"], str(cat["count"]))
182
+
183
+ console.print(table)
184
+
185
+
186
+ def _print_templates_table(templates: list) -> None:
187
+ """Print templates as a Rich table."""
188
+ table = Table(
189
+ title=f"Available Evaluation Templates ({len(templates)} total)",
190
+ show_header=True,
191
+ header_style="bold cyan",
192
+ )
193
+
194
+ table.add_column("Template Name", style="green")
195
+ table.add_column("Category", style="blue")
196
+ table.add_column("Class", style="dim")
197
+
198
+ for template in templates:
199
+ table.add_row(
200
+ template["name"],
201
+ template["category"],
202
+ template["class"],
203
+ )
204
+
205
+ console.print(table)
206
+
207
+
208
+ def _get_template_category(eval_name: str) -> str:
209
+ """Get the category for a template."""
210
+ for category, templates in TEMPLATE_CATEGORIES.items():
211
+ if eval_name in templates:
212
+ return category
213
+ return "other"