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,163 @@
1
+ """
2
+ Noise Sensitivity Metric.
3
+
4
+ Measures RAG system robustness to irrelevant/noisy context
5
+ mixed with relevant context.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from ...base_metric import BaseMetric
11
+ from ..types import NoiseSensitivityInput
12
+ from ..utils import (
13
+ extract_claims,
14
+ check_claim_supported,
15
+ compute_semantic_similarity,
16
+ )
17
+
18
+
19
+ class NoiseSensitivity(BaseMetric[NoiseSensitivityInput]):
20
+ """
21
+ Measures RAG system robustness to irrelevant context.
22
+
23
+ Compares response quality when noise is added to the context.
24
+ Lower sensitivity (higher score) means more robust system.
25
+
26
+ Requires both clean and noisy response variants to compare.
27
+
28
+ Approach:
29
+ 1. Calculate faithfulness of clean response (relevant contexts only)
30
+ 2. Calculate faithfulness of noisy response (relevant + irrelevant contexts)
31
+ 3. Sensitivity = degradation when noise added
32
+ 4. Robustness = 1 - sensitivity (returned as output)
33
+
34
+ Score: 0.0 (very sensitive to noise) to 1.0 (robust to noise)
35
+
36
+ Example:
37
+ >>> noise_sens = NoiseSensitivity()
38
+ >>> result = noise_sens.evaluate([{
39
+ ... "query": "What is the capital of France?",
40
+ ... "response_clean": "The capital of France is Paris.",
41
+ ... "response_noisy": "The capital of France is Paris, a beautiful city.",
42
+ ... "relevant_contexts": ["Paris is the capital of France."],
43
+ ... "irrelevant_contexts": ["The Eiffel Tower is 330 meters tall."],
44
+ ... }])
45
+ """
46
+
47
+ @property
48
+ def metric_name(self) -> str:
49
+ return "noise_sensitivity"
50
+
51
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
52
+ super().__init__(config)
53
+ self.faithfulness_threshold = self.config.get("faithfulness_threshold", 0.5)
54
+
55
+ def compute_one(self, inputs: NoiseSensitivityInput) -> Dict[str, Any]:
56
+ # Validate inputs
57
+ if not inputs.relevant_contexts:
58
+ return {
59
+ "output": 0.0,
60
+ "reason": "No relevant contexts provided",
61
+ }
62
+
63
+ # Calculate faithfulness of clean response
64
+ clean_faithfulness = self._calculate_faithfulness(
65
+ inputs.response_clean, inputs.relevant_contexts
66
+ )
67
+
68
+ # Calculate faithfulness of noisy response
69
+ all_contexts = inputs.relevant_contexts + inputs.irrelevant_contexts
70
+ noisy_faithfulness = self._calculate_faithfulness(
71
+ inputs.response_noisy, all_contexts
72
+ )
73
+
74
+ # Calculate semantic similarity between responses
75
+ response_similarity = compute_semantic_similarity(
76
+ inputs.response_clean, inputs.response_noisy
77
+ )
78
+
79
+ # Calculate sensitivity metrics
80
+ # Sensitivity = degradation when noise added
81
+ faithfulness_degradation = max(0, clean_faithfulness - noisy_faithfulness)
82
+
83
+ # Also check if noisy response introduced irrelevant info
84
+ irrelevant_contamination = self._check_contamination(
85
+ inputs.response_noisy,
86
+ inputs.irrelevant_contexts,
87
+ inputs.relevant_contexts
88
+ )
89
+
90
+ # Combined sensitivity score
91
+ sensitivity = (
92
+ 0.5 * faithfulness_degradation +
93
+ 0.3 * irrelevant_contamination +
94
+ 0.2 * (1.0 - response_similarity)
95
+ )
96
+
97
+ # Convert to robustness score (1 - sensitivity)
98
+ robustness = max(0.0, 1.0 - sensitivity)
99
+
100
+ return {
101
+ "output": round(robustness, 4),
102
+ "reason": f"Robustness: {robustness:.2f} (clean={clean_faithfulness:.2f}, noisy={noisy_faithfulness:.2f})",
103
+ "clean_faithfulness": round(clean_faithfulness, 4),
104
+ "noisy_faithfulness": round(noisy_faithfulness, 4),
105
+ "faithfulness_degradation": round(faithfulness_degradation, 4),
106
+ "irrelevant_contamination": round(irrelevant_contamination, 4),
107
+ "response_similarity": round(response_similarity, 4),
108
+ "sensitivity": round(sensitivity, 4),
109
+ }
110
+
111
+ def _calculate_faithfulness(
112
+ self, response: str, contexts: List[str]
113
+ ) -> float:
114
+ """Calculate faithfulness score for a response given contexts."""
115
+ claims = extract_claims(response)
116
+
117
+ if not claims:
118
+ return 1.0 # No claims to verify
119
+
120
+ supported = 0
121
+ for claim in claims:
122
+ is_supported, score, _ = check_claim_supported(
123
+ claim, contexts, self.faithfulness_threshold
124
+ )
125
+ if is_supported:
126
+ supported += 1
127
+
128
+ return supported / len(claims)
129
+
130
+ def _check_contamination(
131
+ self,
132
+ response: str,
133
+ irrelevant_contexts: List[str],
134
+ relevant_contexts: List[str]
135
+ ) -> float:
136
+ """
137
+ Check if response contains information from irrelevant contexts
138
+ that is NOT in relevant contexts.
139
+ """
140
+ if not irrelevant_contexts:
141
+ return 0.0
142
+
143
+ claims = extract_claims(response)
144
+ if not claims:
145
+ return 0.0
146
+
147
+ contaminated_claims = 0
148
+
149
+ for claim in claims:
150
+ # Check if claim is supported by irrelevant contexts
151
+ irrel_supported, irrel_score, _ = check_claim_supported(
152
+ claim, irrelevant_contexts, 0.4
153
+ )
154
+
155
+ # But NOT supported by relevant contexts
156
+ rel_supported, rel_score, _ = check_claim_supported(
157
+ claim, relevant_contexts, 0.4
158
+ )
159
+
160
+ if irrel_supported and not rel_supported:
161
+ contaminated_claims += 1
162
+
163
+ return contaminated_claims / len(claims)
@@ -0,0 +1,261 @@
1
+ """
2
+ Ranking Metrics for RAG Retrieval.
3
+
4
+ Provides NDCG (Normalized Discounted Cumulative Gain) and
5
+ MRR (Mean Reciprocal Rank) metrics.
6
+ """
7
+
8
+ import math
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from ...base_metric import BaseMetric
12
+ from ..types import RAGRankingInput
13
+
14
+
15
+ class NDCG(BaseMetric[RAGRankingInput]):
16
+ """
17
+ Normalized Discounted Cumulative Gain for ranked retrieval.
18
+
19
+ Accounts for graded relevance (not just binary) and position.
20
+ Higher scores for relevant items appearing early in ranking.
21
+
22
+ Formula:
23
+ DCG@k = Σ (2^rel_i - 1) / log2(i + 2)
24
+ NDCG@k = DCG@k / IDCG@k
25
+
26
+ Where IDCG is the ideal DCG (perfect ranking).
27
+
28
+ Score: 0.0 (worst ranking) to 1.0 (perfect ranking)
29
+
30
+ Example:
31
+ >>> ndcg = NDCG(config={"k": 5})
32
+ >>> result = ndcg.evaluate([{
33
+ ... "query": "machine learning",
34
+ ... "contexts": ["ML intro", "Unrelated", "ML advanced"],
35
+ ... "relevance_scores": [1.0, 0.0, 0.8]
36
+ ... }])
37
+ """
38
+
39
+ @property
40
+ def metric_name(self) -> str:
41
+ return "ndcg"
42
+
43
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
44
+ super().__init__(config)
45
+ self.k = self.config.get("k", None) # None = use all
46
+
47
+ def compute_one(self, inputs: RAGRankingInput) -> Dict[str, Any]:
48
+ scores = inputs.relevance_scores
49
+
50
+ if not scores:
51
+ return {
52
+ "output": 0.0,
53
+ "reason": "No relevance scores provided",
54
+ }
55
+
56
+ k = self.k if self.k is not None else len(scores)
57
+ k = min(k, len(scores))
58
+
59
+ # DCG@k
60
+ dcg = self._compute_dcg(scores[:k])
61
+
62
+ # Ideal DCG@k (perfect ranking - sorted descending)
63
+ ideal_scores = sorted(scores, reverse=True)
64
+ idcg = self._compute_dcg(ideal_scores[:k])
65
+
66
+ if idcg == 0:
67
+ # All scores are 0
68
+ return {
69
+ "output": 0.0,
70
+ "reason": "All relevance scores are 0",
71
+ "dcg": 0.0,
72
+ "idcg": 0.0,
73
+ }
74
+
75
+ ndcg = dcg / idcg
76
+
77
+ return {
78
+ "output": round(ndcg, 4),
79
+ "reason": f"NDCG@{k}={ndcg:.3f}, DCG={dcg:.3f}, IDCG={idcg:.3f}",
80
+ "k": k,
81
+ "dcg": round(dcg, 4),
82
+ "idcg": round(idcg, 4),
83
+ "relevance_scores": scores[:k],
84
+ }
85
+
86
+ def _compute_dcg(self, scores: List[float]) -> float:
87
+ """Compute Discounted Cumulative Gain."""
88
+ dcg = 0.0
89
+ for i, score in enumerate(scores):
90
+ # Position is 1-indexed in the formula
91
+ dcg += (2**score - 1) / math.log2(i + 2)
92
+ return dcg
93
+
94
+
95
+ class MRR(BaseMetric[RAGRankingInput]):
96
+ """
97
+ Mean Reciprocal Rank - how quickly the first relevant result appears.
98
+
99
+ Best for single-answer retrieval tasks where you only need
100
+ one correct result.
101
+
102
+ Formula:
103
+ MRR = 1 / rank_of_first_relevant
104
+
105
+ Score: 0.0 (no relevant results) to 1.0 (first result is relevant)
106
+
107
+ Example:
108
+ >>> mrr = MRR(config={"relevance_threshold": 0.5})
109
+ >>> result = mrr.evaluate([{
110
+ ... "query": "capital of France",
111
+ ... "contexts": ["Unrelated", "Paris is capital", "More info"],
112
+ ... "relevance_scores": [0.1, 0.9, 0.3]
113
+ ... }])
114
+ >>> print(result.eval_results[0].output) # 0.5 (found at position 2)
115
+ """
116
+
117
+ @property
118
+ def metric_name(self) -> str:
119
+ return "mrr"
120
+
121
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
122
+ super().__init__(config)
123
+ self.relevance_threshold = self.config.get("relevance_threshold", 0.5)
124
+
125
+ def compute_one(self, inputs: RAGRankingInput) -> Dict[str, Any]:
126
+ scores = inputs.relevance_scores
127
+
128
+ if not scores:
129
+ return {
130
+ "output": 0.0,
131
+ "reason": "No relevance scores provided",
132
+ }
133
+
134
+ # Find first relevant result
135
+ for i, score in enumerate(scores, 1):
136
+ if score >= self.relevance_threshold:
137
+ reciprocal_rank = 1.0 / i
138
+ return {
139
+ "output": round(reciprocal_rank, 4),
140
+ "reason": f"First relevant result at position {i}",
141
+ "first_relevant_position": i,
142
+ "first_relevant_score": round(score, 4),
143
+ "threshold": self.relevance_threshold,
144
+ }
145
+
146
+ return {
147
+ "output": 0.0,
148
+ "reason": f"No results above relevance threshold ({self.relevance_threshold})",
149
+ "first_relevant_position": None,
150
+ "threshold": self.relevance_threshold,
151
+ }
152
+
153
+
154
+ class PrecisionAtK(BaseMetric[RAGRankingInput]):
155
+ """
156
+ Precision@K - fraction of top-K results that are relevant.
157
+
158
+ Simple metric for evaluating retrieval quality at a fixed cutoff.
159
+
160
+ Formula:
161
+ Precision@K = |relevant in top K| / K
162
+
163
+ Score: 0.0 (no relevant in top K) to 1.0 (all top K are relevant)
164
+ """
165
+
166
+ @property
167
+ def metric_name(self) -> str:
168
+ return "precision_at_k"
169
+
170
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
171
+ super().__init__(config)
172
+ self.k = self.config.get("k", 5)
173
+ self.relevance_threshold = self.config.get("relevance_threshold", 0.5)
174
+
175
+ def compute_one(self, inputs: RAGRankingInput) -> Dict[str, Any]:
176
+ scores = inputs.relevance_scores
177
+
178
+ if not scores:
179
+ return {
180
+ "output": 0.0,
181
+ "reason": "No relevance scores provided",
182
+ }
183
+
184
+ k = min(self.k, len(scores))
185
+ top_k_scores = scores[:k]
186
+
187
+ relevant_count = sum(
188
+ 1 for score in top_k_scores if score >= self.relevance_threshold
189
+ )
190
+
191
+ precision = relevant_count / k
192
+
193
+ return {
194
+ "output": round(precision, 4),
195
+ "reason": f"Precision@{k}={precision:.3f}, {relevant_count}/{k} relevant",
196
+ "k": k,
197
+ "relevant_count": relevant_count,
198
+ "threshold": self.relevance_threshold,
199
+ }
200
+
201
+
202
+ class RecallAtK(BaseMetric[RAGRankingInput]):
203
+ """
204
+ Recall@K - fraction of all relevant results that appear in top K.
205
+
206
+ Measures how many of the relevant items were retrieved.
207
+
208
+ Formula:
209
+ Recall@K = |relevant in top K| / |total relevant|
210
+
211
+ Score: 0.0 (no relevant recalled) to 1.0 (all relevant in top K)
212
+ """
213
+
214
+ @property
215
+ def metric_name(self) -> str:
216
+ return "recall_at_k"
217
+
218
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
219
+ super().__init__(config)
220
+ self.k = self.config.get("k", 5)
221
+ self.relevance_threshold = self.config.get("relevance_threshold", 0.5)
222
+
223
+ def compute_one(self, inputs: RAGRankingInput) -> Dict[str, Any]:
224
+ scores = inputs.relevance_scores
225
+
226
+ if not scores:
227
+ return {
228
+ "output": 0.0,
229
+ "reason": "No relevance scores provided",
230
+ }
231
+
232
+ # Count total relevant
233
+ total_relevant = sum(
234
+ 1 for score in scores if score >= self.relevance_threshold
235
+ )
236
+
237
+ if total_relevant == 0:
238
+ return {
239
+ "output": 0.0,
240
+ "reason": "No relevant items to recall",
241
+ "k": self.k,
242
+ "total_relevant": 0,
243
+ }
244
+
245
+ k = min(self.k, len(scores))
246
+ top_k_scores = scores[:k]
247
+
248
+ recalled = sum(
249
+ 1 for score in top_k_scores if score >= self.relevance_threshold
250
+ )
251
+
252
+ recall = recalled / total_relevant
253
+
254
+ return {
255
+ "output": round(recall, 4),
256
+ "reason": f"Recall@{k}={recall:.3f}, {recalled}/{total_relevant} recalled",
257
+ "k": k,
258
+ "recalled": recalled,
259
+ "total_relevant": total_relevant,
260
+ "threshold": self.relevance_threshold,
261
+ }
@@ -0,0 +1,100 @@
1
+ """
2
+ RAG Evaluation Input/Output Types.
3
+
4
+ Defines strongly-typed inputs for all RAG metrics.
5
+ """
6
+
7
+ from typing import List, Optional
8
+ from pydantic import Field
9
+
10
+ from ...types import BaseMetricInput
11
+
12
+
13
+ class RAGInput(BaseMetricInput):
14
+ """Standard RAG evaluation input for generation metrics."""
15
+
16
+ query: str = Field(..., description="The user query/question")
17
+ response: str = Field(..., description="The generated response")
18
+ contexts: List[str] = Field(..., description="Retrieved context passages")
19
+ reference: Optional[str] = Field(None, description="Ground truth answer")
20
+
21
+
22
+ class RAGRetrievalInput(BaseMetricInput):
23
+ """Input for retrieval-only metrics (recall, precision, entity recall)."""
24
+
25
+ query: str = Field(..., description="The user query")
26
+ contexts: List[str] = Field(..., description="Retrieved context passages")
27
+ reference: str = Field(..., description="Ground truth/reference text")
28
+ relevance_labels: Optional[List[int]] = Field(
29
+ None, description="Binary relevance labels (1=relevant, 0=irrelevant)"
30
+ )
31
+
32
+
33
+ class RAGRankingInput(BaseMetricInput):
34
+ """Input for ranking metrics (NDCG, MRR)."""
35
+
36
+ query: str = Field(..., description="The user query")
37
+ contexts: List[str] = Field(..., description="Retrieved contexts in ranked order")
38
+ relevance_scores: List[float] = Field(
39
+ ..., description="Relevance scores/grades for each context (0-1 or graded)"
40
+ )
41
+
42
+
43
+ class NoiseSensitivityInput(BaseMetricInput):
44
+ """Input for noise sensitivity evaluation."""
45
+
46
+ query: str = Field(..., description="The user query")
47
+ response_clean: str = Field(
48
+ ..., description="Response generated with only relevant contexts"
49
+ )
50
+ response_noisy: str = Field(
51
+ ..., description="Response generated with relevant + irrelevant contexts"
52
+ )
53
+ relevant_contexts: List[str] = Field(..., description="Relevant context passages")
54
+ irrelevant_contexts: List[str] = Field(
55
+ ..., description="Irrelevant/noisy context passages"
56
+ )
57
+ reference: Optional[str] = Field(None, description="Ground truth answer")
58
+
59
+
60
+ class RAGMultiHopInput(BaseMetricInput):
61
+ """Input for multi-hop reasoning evaluation."""
62
+
63
+ query: str = Field(..., description="Complex query requiring multiple hops")
64
+ response: str = Field(..., description="Generated response")
65
+ contexts: List[str] = Field(..., description="Retrieved context passages")
66
+ hop_chain: Optional[List[str]] = Field(
67
+ None, description="Expected reasoning chain across contexts"
68
+ )
69
+ reference: Optional[str] = Field(None, description="Ground truth answer")
70
+
71
+
72
+ class SourceAttributionInput(BaseMetricInput):
73
+ """Input for source attribution evaluation."""
74
+
75
+ response: str = Field(..., description="Generated response with citations")
76
+ contexts: List[str] = Field(..., description="Source documents")
77
+ citation_format: str = Field(
78
+ "bracketed", description="Citation format: bracketed [1], inline, footnote"
79
+ )
80
+ require_citations: bool = Field(
81
+ True, description="Whether citations are required for all claims"
82
+ )
83
+
84
+
85
+ class ContextUtilizationInput(BaseMetricInput):
86
+ """Input for context utilization evaluation."""
87
+
88
+ query: str = Field(..., description="The user query")
89
+ response: str = Field(..., description="The generated response")
90
+ contexts: List[str] = Field(..., description="Retrieved context passages")
91
+
92
+
93
+ class AnswerRelevancyInput(BaseMetricInput):
94
+ """Input for answer relevancy evaluation."""
95
+
96
+ query: str = Field(..., description="The user query")
97
+ response: str = Field(..., description="The generated response")
98
+ contexts: Optional[List[str]] = Field(
99
+ None, description="Retrieved context passages (optional)"
100
+ )
@@ -0,0 +1,62 @@
1
+ """
2
+ RAG Evaluation Utilities.
3
+
4
+ Provides common functions for NLI, claim extraction,
5
+ entity extraction, and semantic similarity.
6
+ """
7
+
8
+ from .nli import (
9
+ NLILabel,
10
+ check_entailment,
11
+ check_entailment_heuristic,
12
+ check_claim_supported,
13
+ check_attribution,
14
+ check_contradiction,
15
+ )
16
+ from .claims import (
17
+ split_into_sentences,
18
+ extract_claims,
19
+ extract_key_phrases,
20
+ extract_atomic_claims,
21
+ )
22
+ from .entities import (
23
+ extract_entities,
24
+ extract_entities_heuristic,
25
+ entities_match,
26
+ normalize_entity,
27
+ )
28
+ from .similarity import (
29
+ compute_text_similarity,
30
+ compute_word_overlap,
31
+ compute_semantic_similarity,
32
+ compute_ngram_overlap,
33
+ extract_keywords,
34
+ normalize_text,
35
+ )
36
+
37
+ __all__ = [
38
+ # NLI
39
+ "NLILabel",
40
+ "check_entailment",
41
+ "check_entailment_heuristic",
42
+ "check_claim_supported",
43
+ "check_attribution",
44
+ "check_contradiction",
45
+ # Claims
46
+ "split_into_sentences",
47
+ "extract_claims",
48
+ "extract_key_phrases",
49
+ "extract_atomic_claims",
50
+ # Entities
51
+ "extract_entities",
52
+ "extract_entities_heuristic",
53
+ "entities_match",
54
+ "normalize_entity",
55
+ # Similarity
56
+ "compute_text_similarity",
57
+ "compute_word_overlap",
58
+ "compute_semantic_similarity",
59
+ "compute_ngram_overlap",
60
+ "extract_keywords",
61
+ "normalize_text",
62
+ ]