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,277 @@
1
+ """
2
+ Comprehensive RAG Score Metric.
3
+
4
+ Combines multiple RAG metrics into a single comprehensive score.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional
8
+
9
+ from ..base_metric import BaseMetric
10
+ from .types import RAGInput, RAGRetrievalInput
11
+ from .retrieval import ContextRecall, ContextPrecision
12
+ from .generation import AnswerRelevancy, ContextUtilization, RAGFaithfulness
13
+
14
+
15
+ class RAGScore(BaseMetric[RAGInput]):
16
+ """
17
+ Comprehensive RAG evaluation combining all metrics.
18
+
19
+ Evaluates both retrieval and generation components in a single call.
20
+
21
+ Default weights (configurable):
22
+ - Retrieval (40%): Context Recall, Context Precision
23
+ - Generation (40%): Faithfulness, Answer Relevancy
24
+ - Advanced (20%): Context Utilization
25
+
26
+ Score: 0.0 (poor RAG performance) to 1.0 (excellent RAG performance)
27
+
28
+ Example:
29
+ >>> rag_score = RAGScore()
30
+ >>> result = rag_score.evaluate([{
31
+ ... "query": "What is the capital of France?",
32
+ ... "response": "The capital of France is Paris.",
33
+ ... "contexts": ["Paris is the capital and largest city of France."],
34
+ ... "reference": "Paris is the capital of France."
35
+ ... }])
36
+ """
37
+
38
+ @property
39
+ def metric_name(self) -> str:
40
+ return "rag_score"
41
+
42
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
43
+ super().__init__(config)
44
+ self.weights = self.config.get("weights", {
45
+ "retrieval": 0.4,
46
+ "generation": 0.4,
47
+ "advanced": 0.2,
48
+ })
49
+ self.include_details = self.config.get("include_details", True)
50
+
51
+ def compute_one(self, inputs: RAGInput) -> Dict[str, Any]:
52
+ scores = {}
53
+ details = {}
54
+
55
+ # Prepare retrieval input — only if reference is available
56
+ reference = inputs.reference
57
+ has_reference = bool(reference and reference.strip())
58
+
59
+ if has_reference:
60
+ retrieval_input = RAGRetrievalInput(
61
+ query=inputs.query,
62
+ contexts=inputs.contexts,
63
+ reference=reference,
64
+ )
65
+
66
+ recall_metric = ContextRecall()
67
+ recall_result = recall_metric.compute_one(retrieval_input)
68
+ scores["context_recall"] = recall_result["output"]
69
+ if self.include_details:
70
+ details["context_recall"] = recall_result.get("reason", "")
71
+
72
+ precision_metric = ContextPrecision()
73
+ precision_result = precision_metric.compute_one(retrieval_input)
74
+ scores["context_precision"] = precision_result["output"]
75
+ if self.include_details:
76
+ details["context_precision"] = precision_result.get("reason", "")
77
+ else:
78
+ scores["context_recall"] = 0.0
79
+ scores["context_precision"] = 0.0
80
+ if self.include_details:
81
+ details["context_recall"] = "No reference provided — skipped"
82
+ details["context_precision"] = "No reference provided — skipped"
83
+
84
+ retrieval_score = (scores["context_recall"] + scores["context_precision"]) / 2
85
+
86
+ # Generation metrics
87
+ from .types import AnswerRelevancyInput, ContextUtilizationInput
88
+
89
+ relevancy_input = AnswerRelevancyInput(
90
+ query=inputs.query,
91
+ response=inputs.response,
92
+ contexts=inputs.contexts,
93
+ )
94
+ relevancy_metric = AnswerRelevancy()
95
+ relevancy_result = relevancy_metric.compute_one(relevancy_input)
96
+ scores["answer_relevancy"] = relevancy_result["output"]
97
+ if self.include_details:
98
+ details["answer_relevancy"] = relevancy_result.get("reason", "")
99
+
100
+ faithfulness_metric = RAGFaithfulness()
101
+ faithfulness_result = faithfulness_metric.compute_one(inputs)
102
+ scores["faithfulness"] = faithfulness_result["output"]
103
+ if self.include_details:
104
+ details["faithfulness"] = faithfulness_result.get("reason", "")
105
+
106
+ generation_score = (scores["answer_relevancy"] + scores["faithfulness"]) / 2
107
+
108
+ # Advanced metrics
109
+ utilization_input = ContextUtilizationInput(
110
+ query=inputs.query,
111
+ response=inputs.response,
112
+ contexts=inputs.contexts,
113
+ )
114
+ utilization_metric = ContextUtilization()
115
+ utilization_result = utilization_metric.compute_one(utilization_input)
116
+ scores["context_utilization"] = utilization_result["output"]
117
+ if self.include_details:
118
+ details["context_utilization"] = utilization_result.get("reason", "")
119
+
120
+ advanced_score = scores["context_utilization"]
121
+
122
+ # Weighted combination
123
+ final_score = (
124
+ self.weights["retrieval"] * retrieval_score +
125
+ self.weights["generation"] * generation_score +
126
+ self.weights["advanced"] * advanced_score
127
+ )
128
+
129
+ result = {
130
+ "output": round(final_score, 4),
131
+ "reason": f"Retrieval={retrieval_score:.2f}, Generation={generation_score:.2f}, Advanced={advanced_score:.2f}",
132
+ "retrieval_score": round(retrieval_score, 4),
133
+ "generation_score": round(generation_score, 4),
134
+ "advanced_score": round(advanced_score, 4),
135
+ "component_scores": {k: round(v, 4) for k, v in scores.items()},
136
+ }
137
+
138
+ if self.include_details:
139
+ result["component_details"] = details
140
+
141
+ return result
142
+
143
+
144
+ class RAGScoreDetailed(BaseMetric[RAGInput]):
145
+ """
146
+ Detailed RAG evaluation with all available metrics.
147
+
148
+ Runs all RAG metrics and provides comprehensive analysis.
149
+ More expensive but gives complete picture.
150
+
151
+ Components:
152
+ - Retrieval: Recall, Precision, Entity Recall
153
+ - Generation: Faithfulness, Relevancy, Groundedness
154
+ - Advanced: Context Utilization, Multi-hop (if applicable)
155
+
156
+ Score: 0.0 to 1.0
157
+ """
158
+
159
+ @property
160
+ def metric_name(self) -> str:
161
+ return "rag_score_detailed"
162
+
163
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
164
+ super().__init__(config)
165
+ self.retrieval_weight = self.config.get("retrieval_weight", 0.35)
166
+ self.generation_weight = self.config.get("generation_weight", 0.35)
167
+ self.advanced_weight = self.config.get("advanced_weight", 0.30)
168
+
169
+ def compute_one(self, inputs: RAGInput) -> Dict[str, Any]:
170
+ from .retrieval import ContextRecall, ContextPrecision, ContextEntityRecall
171
+ from .generation import AnswerRelevancy, ContextUtilization, Groundedness, RAGFaithfulness
172
+ from .types import AnswerRelevancyInput, ContextUtilizationInput
173
+
174
+ all_scores = {}
175
+ all_details = {}
176
+
177
+ # Prepare retrieval input — only if reference is available
178
+ reference = inputs.reference
179
+ has_reference = bool(reference and reference.strip())
180
+
181
+ if has_reference:
182
+ retrieval_input = RAGRetrievalInput(
183
+ query=inputs.query,
184
+ contexts=inputs.contexts,
185
+ reference=reference,
186
+ )
187
+
188
+ # === RETRIEVAL METRICS ===
189
+ recall = ContextRecall().compute_one(retrieval_input)
190
+ all_scores["context_recall"] = recall["output"]
191
+ all_details["context_recall"] = recall
192
+
193
+ precision = ContextPrecision().compute_one(retrieval_input)
194
+ all_scores["context_precision"] = precision["output"]
195
+ all_details["context_precision"] = precision
196
+
197
+ entity_recall = ContextEntityRecall().compute_one(retrieval_input)
198
+ all_scores["context_entity_recall"] = entity_recall["output"]
199
+ all_details["context_entity_recall"] = entity_recall
200
+ else:
201
+ for key in ("context_recall", "context_precision", "context_entity_recall"):
202
+ all_scores[key] = 0.0
203
+ all_details[key] = {"output": 0.0, "reason": "No reference provided — skipped"}
204
+
205
+ retrieval_avg = (
206
+ all_scores["context_recall"] +
207
+ all_scores["context_precision"] +
208
+ all_scores["context_entity_recall"]
209
+ ) / 3
210
+
211
+ # === GENERATION METRICS ===
212
+ # Faithfulness
213
+ faithfulness = RAGFaithfulness().compute_one(inputs)
214
+ all_scores["faithfulness"] = faithfulness["output"]
215
+ all_details["faithfulness"] = faithfulness
216
+
217
+ # Answer Relevancy
218
+ relevancy_input = AnswerRelevancyInput(
219
+ query=inputs.query,
220
+ response=inputs.response,
221
+ contexts=inputs.contexts,
222
+ )
223
+ relevancy = AnswerRelevancy().compute_one(relevancy_input)
224
+ all_scores["answer_relevancy"] = relevancy["output"]
225
+ all_details["answer_relevancy"] = relevancy
226
+
227
+ # Groundedness
228
+ groundedness = Groundedness().compute_one(inputs)
229
+ all_scores["groundedness"] = groundedness["output"]
230
+ all_details["groundedness"] = groundedness
231
+
232
+ generation_avg = (
233
+ all_scores["faithfulness"] +
234
+ all_scores["answer_relevancy"] +
235
+ all_scores["groundedness"]
236
+ ) / 3
237
+
238
+ # === ADVANCED METRICS ===
239
+ # Context Utilization
240
+ util_input = ContextUtilizationInput(
241
+ query=inputs.query,
242
+ response=inputs.response,
243
+ contexts=inputs.contexts,
244
+ )
245
+ utilization = ContextUtilization().compute_one(util_input)
246
+ all_scores["context_utilization"] = utilization["output"]
247
+ all_details["context_utilization"] = utilization
248
+
249
+ advanced_avg = all_scores["context_utilization"]
250
+
251
+ # === FINAL SCORE ===
252
+ final_score = (
253
+ self.retrieval_weight * retrieval_avg +
254
+ self.generation_weight * generation_avg +
255
+ self.advanced_weight * advanced_avg
256
+ )
257
+
258
+ # Determine quality level
259
+ if final_score >= 0.8:
260
+ quality = "excellent"
261
+ elif final_score >= 0.6:
262
+ quality = "good"
263
+ elif final_score >= 0.4:
264
+ quality = "fair"
265
+ else:
266
+ quality = "poor"
267
+
268
+ return {
269
+ "output": round(final_score, 4),
270
+ "reason": f"Quality: {quality} - Retrieval={retrieval_avg:.2f}, Generation={generation_avg:.2f}, Advanced={advanced_avg:.2f}",
271
+ "quality_level": quality,
272
+ "retrieval_average": round(retrieval_avg, 4),
273
+ "generation_average": round(generation_avg, 4),
274
+ "advanced_average": round(advanced_avg, 4),
275
+ "all_scores": {k: round(v, 4) for k, v in all_scores.items()},
276
+ "details": all_details,
277
+ }
@@ -0,0 +1,20 @@
1
+ """
2
+ RAG Retrieval Metrics.
3
+
4
+ Metrics for evaluating the retrieval component of RAG systems.
5
+ """
6
+
7
+ from .context_recall import ContextRecall
8
+ from .context_precision import ContextPrecision
9
+ from .context_entity_recall import ContextEntityRecall
10
+ from .noise_sensitivity import NoiseSensitivity
11
+ from .ranking import NDCG, MRR
12
+
13
+ __all__ = [
14
+ "ContextRecall",
15
+ "ContextPrecision",
16
+ "ContextEntityRecall",
17
+ "NoiseSensitivity",
18
+ "NDCG",
19
+ "MRR",
20
+ ]
@@ -0,0 +1,124 @@
1
+ """
2
+ Context Entity Recall Metric.
3
+
4
+ Measures entity-level retrieval coverage - what fraction of
5
+ entities from the reference appear in retrieved contexts.
6
+ """
7
+
8
+ from typing import Any, Dict, Optional, Set
9
+
10
+ from ...base_metric import BaseMetric
11
+ from ..types import RAGRetrievalInput
12
+ from ..utils import extract_entities, entities_match
13
+
14
+
15
+ class ContextEntityRecall(BaseMetric[RAGRetrievalInput]):
16
+ """
17
+ Measures entity-level retrieval completeness.
18
+
19
+ Uses NER to extract entities from reference and contexts,
20
+ then calculates what fraction of reference entities appear
21
+ in the retrieved contexts.
22
+
23
+ Critical for fact-based use cases where specific entities
24
+ (names, dates, locations, amounts) must not be missed.
25
+
26
+ Formula:
27
+ Context Entity Recall = |Entities in Context ∩ Entities in Reference| / |Entities in Reference|
28
+
29
+ Score: 0.0 (no entities recalled) to 1.0 (all entities recalled)
30
+
31
+ Example:
32
+ >>> entity_recall = ContextEntityRecall()
33
+ >>> result = entity_recall.evaluate([{
34
+ ... "query": "When did Einstein win the Nobel Prize?",
35
+ ... "contexts": ["Albert Einstein received the Nobel Prize in Physics in 1921."],
36
+ ... "reference": "Albert Einstein won the Nobel Prize in 1921."
37
+ ... }])
38
+ """
39
+
40
+ @property
41
+ def metric_name(self) -> str:
42
+ return "context_entity_recall"
43
+
44
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
45
+ super().__init__(config)
46
+ self.entity_types = self.config.get("entity_types", None) # None = all types
47
+ self.fuzzy_match = self.config.get("fuzzy_match", True)
48
+ self.case_sensitive = self.config.get("case_sensitive", False)
49
+
50
+ def compute_one(self, inputs: RAGRetrievalInput) -> Dict[str, Any]:
51
+ # Handle empty inputs
52
+ if not inputs.reference or not inputs.reference.strip():
53
+ return {
54
+ "output": 0.0,
55
+ "reason": "No reference provided — cannot measure entity recall",
56
+ }
57
+
58
+ # Extract entities from reference
59
+ reference_entities = extract_entities(
60
+ inputs.reference,
61
+ entity_types=self.entity_types,
62
+ )
63
+
64
+ if not reference_entities:
65
+ return {
66
+ "output": 0.0,
67
+ "reason": "No entities found in reference",
68
+ "reference_entities": [],
69
+ }
70
+
71
+ if not inputs.contexts:
72
+ return {
73
+ "output": 0.0,
74
+ "reason": "No contexts provided - cannot recall any entities",
75
+ "reference_entities": list(reference_entities),
76
+ "recalled_entities": [],
77
+ "missing_entities": list(reference_entities),
78
+ }
79
+
80
+ # Extract entities from all contexts
81
+ context_entities: Set[str] = set()
82
+ for ctx in inputs.contexts:
83
+ ctx_ents = extract_entities(ctx, entity_types=self.entity_types)
84
+ context_entities.update(ctx_ents)
85
+
86
+ # Calculate entity overlap
87
+ recalled = set()
88
+ missing = set()
89
+
90
+ for ref_entity in reference_entities:
91
+ found = False
92
+
93
+ if self.fuzzy_match:
94
+ # Check for fuzzy matches
95
+ for ctx_entity in context_entities:
96
+ if entities_match(ref_entity, ctx_entity):
97
+ found = True
98
+ break
99
+ else:
100
+ # Exact match (case-insensitive by default)
101
+ if self.case_sensitive:
102
+ found = ref_entity in context_entities
103
+ else:
104
+ found = ref_entity.lower() in {e.lower() for e in context_entities}
105
+
106
+ if found:
107
+ recalled.add(ref_entity)
108
+ else:
109
+ missing.add(ref_entity)
110
+
111
+ # Calculate recall
112
+ recall = len(recalled) / len(reference_entities)
113
+
114
+ return {
115
+ "output": round(recall, 4),
116
+ "reason": f"{len(recalled)}/{len(reference_entities)} entities recalled",
117
+ "total_reference_entities": len(reference_entities),
118
+ "recalled_count": len(recalled),
119
+ "missing_count": len(missing),
120
+ "reference_entities": list(reference_entities),
121
+ "recalled_entities": list(recalled),
122
+ "missing_entities": list(missing),
123
+ "context_entities": list(context_entities)[:20], # Limit for readability
124
+ }
@@ -0,0 +1,158 @@
1
+ """
2
+ Context Precision Metric.
3
+
4
+ Measures retrieval ranking quality - whether relevant contexts
5
+ appear before irrelevant ones.
6
+ """
7
+
8
+ from typing import Any, Dict, Optional
9
+
10
+ from ...base_metric import BaseMetric
11
+ from ..types import RAGRetrievalInput
12
+ from ..utils import (
13
+ compute_semantic_similarity,
14
+ compute_word_overlap,
15
+ check_entailment,
16
+ NLILabel,
17
+ )
18
+
19
+
20
+ class ContextPrecision(BaseMetric[RAGRetrievalInput]):
21
+ """
22
+ Measures retrieval ranking quality.
23
+
24
+ Evaluates whether relevant contexts are ranked higher than
25
+ irrelevant ones. A single irrelevant chunk at position 1
26
+ significantly reduces the score vs. the same chunk at position 5.
27
+
28
+ Formula (Average Precision style):
29
+ Context Precision@K = Σ(Precision@k × v_k) / |Relevant items in top K|
30
+
31
+ Where:
32
+ - Precision@k = TP@k / (TP@k + FP@k)
33
+ - v_k ∈ {0,1} indicates relevance at rank k
34
+
35
+ Score: 0.0 (poor ranking) to 1.0 (perfect ranking)
36
+
37
+ Example:
38
+ >>> precision = ContextPrecision()
39
+ >>> result = precision.evaluate([{
40
+ ... "query": "What is machine learning?",
41
+ ... "contexts": [
42
+ ... "Machine learning is a branch of AI.", # Relevant
43
+ ... "The weather is nice today.", # Irrelevant
44
+ ... ],
45
+ ... "reference": "Machine learning is an AI technique."
46
+ ... }])
47
+ """
48
+
49
+ @property
50
+ def metric_name(self) -> str:
51
+ return "context_precision"
52
+
53
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
54
+ super().__init__(config)
55
+ self.relevance_threshold = self.config.get("relevance_threshold", 0.3)
56
+ self.use_reference = self.config.get("use_reference", True)
57
+
58
+ def compute_one(self, inputs: RAGRetrievalInput) -> Dict[str, Any]:
59
+ if not inputs.contexts:
60
+ return {
61
+ "output": 0.0,
62
+ "reason": "No contexts to evaluate",
63
+ }
64
+
65
+ # Determine relevance of each context
66
+ relevance = []
67
+ relevance_details = []
68
+
69
+ for i, ctx in enumerate(inputs.contexts):
70
+ if inputs.relevance_labels is not None and i < len(inputs.relevance_labels):
71
+ # Use provided labels
72
+ is_relevant = inputs.relevance_labels[i] == 1
73
+ relevance_score = 1.0 if is_relevant else 0.0
74
+ else:
75
+ # Compute relevance
76
+ relevance_score = self._compute_relevance(
77
+ ctx, inputs.reference, inputs.query
78
+ )
79
+ is_relevant = relevance_score >= self.relevance_threshold
80
+
81
+ relevance.append(1 if is_relevant else 0)
82
+ relevance_details.append({
83
+ "position": i + 1,
84
+ "relevant": is_relevant,
85
+ "score": round(relevance_score, 3),
86
+ "context_preview": ctx[:80] + "..." if len(ctx) > 80 else ctx,
87
+ })
88
+
89
+ # Calculate Average Precision
90
+ precision_sum = 0.0
91
+ relevant_count = 0
92
+
93
+ for k, is_rel in enumerate(relevance, 1):
94
+ if is_rel:
95
+ relevant_count += 1
96
+ precision_at_k = relevant_count / k
97
+ precision_sum += precision_at_k
98
+
99
+ total_relevant = sum(relevance)
100
+ if total_relevant == 0:
101
+ return {
102
+ "output": 0.0,
103
+ "reason": "No relevant contexts found",
104
+ "relevance_by_position": relevance,
105
+ "details": relevance_details,
106
+ }
107
+
108
+ avg_precision = precision_sum / total_relevant
109
+
110
+ return {
111
+ "output": round(avg_precision, 4),
112
+ "reason": f"AP={avg_precision:.3f}, {total_relevant}/{len(relevance)} contexts relevant",
113
+ "total_contexts": len(relevance),
114
+ "relevant_contexts": total_relevant,
115
+ "relevance_by_position": relevance,
116
+ "details": relevance_details,
117
+ }
118
+
119
+ def _compute_relevance(
120
+ self, context: str, reference: str, query: str
121
+ ) -> float:
122
+ """
123
+ Compute relevance score for a context.
124
+
125
+ Combines multiple signals:
126
+ - Semantic similarity to reference
127
+ - Word overlap with reference
128
+ - Query relevance (if reference unavailable)
129
+ """
130
+ scores = []
131
+
132
+ if reference:
133
+ # Semantic similarity to reference
134
+ sem_sim = compute_semantic_similarity(context, reference)
135
+ scores.append(sem_sim)
136
+
137
+ # Word overlap with reference
138
+ word_overlap = compute_word_overlap(context, reference)
139
+ scores.append(word_overlap)
140
+
141
+ # NLI-based check
142
+ label, nli_score = check_entailment(context, reference)
143
+ if label == NLILabel.ENTAILMENT:
144
+ scores.append(nli_score)
145
+ elif label == NLILabel.CONTRADICTION:
146
+ scores.append(0.0)
147
+ else:
148
+ scores.append(nli_score * 0.5)
149
+
150
+ # Query relevance as fallback or additional signal
151
+ if query:
152
+ query_sim = compute_semantic_similarity(context, query)
153
+ scores.append(query_sim * 0.8) # Slightly lower weight
154
+
155
+ if not scores:
156
+ return 0.0
157
+
158
+ return sum(scores) / len(scores)
@@ -0,0 +1,106 @@
1
+ """
2
+ Context Recall Metric.
3
+
4
+ Measures retrieval completeness - how much of the ground truth
5
+ information is covered by retrieved contexts.
6
+ """
7
+
8
+ from typing import Any, Dict, Optional
9
+
10
+ from ...base_metric import BaseMetric
11
+ from ..types import RAGRetrievalInput
12
+ from ..utils import split_into_sentences, check_attribution
13
+
14
+
15
+ class ContextRecall(BaseMetric[RAGRetrievalInput]):
16
+ """
17
+ Measures retrieval completeness.
18
+
19
+ Evaluates whether retrieved contexts contain all information
20
+ required to produce the ground truth answer.
21
+
22
+ Uses NLI-based attribution to check if reference sentences
23
+ can be inferred from the retrieved contexts.
24
+
25
+ Formula (Ragas-style):
26
+ Context Recall = |GT sentences attributable to context| / |GT sentences|
27
+
28
+ Score: 0.0 (no coverage) to 1.0 (complete coverage)
29
+
30
+ Example:
31
+ >>> recall = ContextRecall()
32
+ >>> result = recall.evaluate([{
33
+ ... "query": "What is the capital of France?",
34
+ ... "contexts": ["Paris is the capital of France."],
35
+ ... "reference": "The capital of France is Paris."
36
+ ... }])
37
+ >>> print(result.eval_results[0].output) # Close to 1.0
38
+ """
39
+
40
+ @property
41
+ def metric_name(self) -> str:
42
+ return "context_recall"
43
+
44
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
45
+ super().__init__(config)
46
+ self.attribution_threshold = self.config.get("attribution_threshold", 0.5)
47
+ self.min_sentence_words = self.config.get("min_sentence_words", 3)
48
+
49
+ def compute_one(self, inputs: RAGRetrievalInput) -> Dict[str, Any]:
50
+ # Handle empty inputs
51
+ if not inputs.reference or not inputs.reference.strip():
52
+ return {
53
+ "output": 0.0,
54
+ "reason": "No reference provided — cannot measure recall",
55
+ }
56
+
57
+ if not inputs.contexts:
58
+ return {
59
+ "output": 0.0,
60
+ "reason": "No contexts provided - cannot recall any information",
61
+ }
62
+
63
+ # Split reference into sentences
64
+ reference_sentences = split_into_sentences(inputs.reference)
65
+
66
+ # Filter very short sentences
67
+ reference_sentences = [
68
+ s for s in reference_sentences
69
+ if len(s.split()) >= self.min_sentence_words
70
+ ]
71
+
72
+ if not reference_sentences:
73
+ return {
74
+ "output": 0.0,
75
+ "reason": "No verifiable sentences in reference",
76
+ }
77
+
78
+ # For each sentence, check attribution to any context
79
+ attributed = 0
80
+ attribution_details = []
81
+
82
+ for sentence in reference_sentences:
83
+ is_attributed, best_context, score = check_attribution(
84
+ sentence, inputs.contexts, self.attribution_threshold
85
+ )
86
+
87
+ if is_attributed:
88
+ attributed += 1
89
+
90
+ attribution_details.append({
91
+ "sentence": sentence[:100] + "..." if len(sentence) > 100 else sentence,
92
+ "attributed": is_attributed,
93
+ "score": round(score, 3),
94
+ "matched_context": best_context[:80] + "..." if best_context and len(best_context) > 80 else best_context,
95
+ })
96
+
97
+ # Calculate recall
98
+ recall = attributed / len(reference_sentences)
99
+
100
+ return {
101
+ "output": round(recall, 4),
102
+ "reason": f"{attributed}/{len(reference_sentences)} reference sentences found in context",
103
+ "total_sentences": len(reference_sentences),
104
+ "attributed_sentences": attributed,
105
+ "details": attribution_details,
106
+ }