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.
- agent_learning_kit-0.1.0.dist-info/METADATA +381 -0
- agent_learning_kit-0.1.0.dist-info/RECORD +642 -0
- agent_learning_kit-0.1.0.dist-info/WHEEL +4 -0
- agent_learning_kit-0.1.0.dist-info/entry_points.txt +5 -0
- agent_learning_kit-0.1.0.dist-info/licenses/LICENSE +173 -0
- agent_learning_kit-0.1.0.dist-info/licenses/NOTICE +7 -0
- fi/__init__.py +5 -0
- fi/alk/__init__.py +57 -0
- fi/alk/_facade.py +31 -0
- fi/alk/_module_alias.py +68 -0
- fi/alk/_paths.py +14 -0
- fi/alk/_schema.py +522 -0
- fi/alk/actions.py +727 -0
- fi/alk/bench/__init__.py +517 -0
- fi/alk/bench/_codeexec.py +213 -0
- fi/alk/bench/_coding.py +215 -0
- fi/alk/bench/_docker.py +237 -0
- fi/alk/bench/_grader.py +286 -0
- fi/alk/bench/_pull.py +212 -0
- fi/alk/bench/_voice.py +147 -0
- fi/alk/capabilities.py +627 -0
- fi/alk/cli.py +6396 -0
- fi/alk/config.py +130 -0
- fi/alk/cua_loop.py +562 -0
- fi/alk/evals.py +2351 -0
- fi/alk/extensions.py +163 -0
- fi/alk/harness/ARCHITECTURE.md +231 -0
- fi/alk/harness/DESIGN.md +246 -0
- fi/alk/harness/ENVIRONMENT_CONFORMANCE.md +127 -0
- fi/alk/harness/HOW-IT-WORKS.md +297 -0
- fi/alk/harness/IMPLEMENTATION_AND_VALIDATION_STATUS.md +229 -0
- fi/alk/harness/README.md +417 -0
- fi/alk/harness/__init__.py +77 -0
- fi/alk/harness/__main__.py +3 -0
- fi/alk/harness/amend.py +312 -0
- fi/alk/harness/artifacts.py +319 -0
- fi/alk/harness/authoring_entrypoint.py +189 -0
- fi/alk/harness/authoring_runtime_validation.py +267 -0
- fi/alk/harness/backends/README.md +43 -0
- fi/alk/harness/backends/__init__.py +122 -0
- fi/alk/harness/backends/base.py +241 -0
- fi/alk/harness/backends/claude.py +211 -0
- fi/alk/harness/backends/files.py +182 -0
- fi/alk/harness/backends/vertex_gemini.py +457 -0
- fi/alk/harness/background_noise.py +95 -0
- fi/alk/harness/build.py +385 -0
- fi/alk/harness/bundle.py +593 -0
- fi/alk/harness/bundle_author_v2.py +1831 -0
- fi/alk/harness/bundle_v2.py +719 -0
- fi/alk/harness/call_runner.py +1440 -0
- fi/alk/harness/callback_http_adapter.py +111 -0
- fi/alk/harness/catalogue.py +287 -0
- fi/alk/harness/chat.py +428 -0
- fi/alk/harness/chat_call_runner.py +506 -0
- fi/alk/harness/checks.py +136 -0
- fi/alk/harness/cli.py +1354 -0
- fi/alk/harness/config.py +338 -0
- fi/alk/harness/contract.py +718 -0
- fi/alk/harness/credentials.py +674 -0
- fi/alk/harness/data/persona_vocabulary.json +111 -0
- fi/alk/harness/environment.py +99 -0
- fi/alk/harness/environment_plan.py +168 -0
- fi/alk/harness/events.py +125 -0
- fi/alk/harness/executor.py +304 -0
- fi/alk/harness/folder.py +234 -0
- fi/alk/harness/generated_runtime.py +815 -0
- fi/alk/harness/github.py +72 -0
- fi/alk/harness/hosted_authoring_entrypoint.py +183 -0
- fi/alk/harness/hosted_entrypoint.py +2402 -0
- fi/alk/harness/hosted_scheduler.py +2218 -0
- fi/alk/harness/job.py +426 -0
- fi/alk/harness/judge.py +184 -0
- fi/alk/harness/livekit_source.py +50 -0
- fi/alk/harness/livekit_tool_trace_bootstrap.py +71 -0
- fi/alk/harness/observability.py +208 -0
- fi/alk/harness/outbound.py +3252 -0
- fi/alk/harness/packaging.py +515 -0
- fi/alk/harness/persona_guides.py +157 -0
- fi/alk/harness/platform.py +692 -0
- fi/alk/harness/process_preflight.py +764 -0
- fi/alk/harness/process_runtime.py +5670 -0
- fi/alk/harness/prove.py +425 -0
- fi/alk/harness/provider_import.py +703 -0
- fi/alk/harness/provider_lifecycle.py +392 -0
- fi/alk/harness/provision.py +2896 -0
- fi/alk/harness/reception.py +147 -0
- fi/alk/harness/retell_chat_call_runner.py +373 -0
- fi/alk/harness/run/__init__.py +296 -0
- fi/alk/harness/run/alk.py +184 -0
- fi/alk/harness/run/call.py +162 -0
- fi/alk/harness/run/conversation.py +264 -0
- fi/alk/harness/run/data/voices_by_language_and_gender.json +693 -0
- fi/alk/harness/run/evidence.py +195 -0
- fi/alk/harness/run/grade.py +598 -0
- fi/alk/harness/run/live.py +297 -0
- fi/alk/harness/run/models.py +56 -0
- fi/alk/harness/run/platform_evals.py +227 -0
- fi/alk/harness/run/sdk_voice.py +130 -0
- fi/alk/harness/run/simulation.py +1209 -0
- fi/alk/harness/run/stage.py +91 -0
- fi/alk/harness/run/targets.py +508 -0
- fi/alk/harness/run/tools.py +601 -0
- fi/alk/harness/run/voice.py +340 -0
- fi/alk/harness/runtime.py +172 -0
- fi/alk/harness/sandbox_server.py +2011 -0
- fi/alk/harness/sandbox_worker.py +44 -0
- fi/alk/harness/scenario.py +1048 -0
- fi/alk/harness/scenario_source.py +879 -0
- fi/alk/harness/scenario_tools.py +1143 -0
- fi/alk/harness/scenarios.py +915 -0
- fi/alk/harness/secrets.py +168 -0
- fi/alk/harness/service_catalog.py +97 -0
- fi/alk/harness/session.py +391 -0
- fi/alk/harness/sessions.py +372 -0
- fi/alk/harness/simulator.py +76 -0
- fi/alk/harness/simulator_voice.py +928 -0
- fi/alk/harness/skills/build-environment/SKILL.md +538 -0
- fi/alk/harness/skills/harness.md +131 -0
- fi/alk/harness/skills/kinds/chat.md +48 -0
- fi/alk/harness/skills/kinds/voice-voicemail.md +63 -0
- fi/alk/harness/skills/kinds/voice.md +59 -0
- fi/alk/harness/skills/plan-suite/SKILL.md +103 -0
- fi/alk/harness/skills/provision-environment/SKILL.md +136 -0
- fi/alk/harness/skills/run-scenarios/SKILL.md +112 -0
- fi/alk/harness/skills/understand-agent/SKILL.md +251 -0
- fi/alk/harness/skills/write-scenarios/SKILL.md +606 -0
- fi/alk/harness/skills/write-scenarios/references/refusals.md +28 -0
- fi/alk/harness/skills/write-scenarios/references/world-api.md +92 -0
- fi/alk/harness/source_data_invariants.py +444 -0
- fi/alk/harness/source_tool_evidence.py +79 -0
- fi/alk/harness/sources.py +253 -0
- fi/alk/harness/spend.py +140 -0
- fi/alk/harness/tool_trace_proxy.py +104 -0
- fi/alk/harness/tools.py +1018 -0
- fi/alk/harness/understand.py +169 -0
- fi/alk/harness/voicemail_audio.py +74 -0
- fi/alk/harness/world/__init__.py +33 -0
- fi/alk/harness/world/errors.py +68 -0
- fi/alk/harness/world/expectations.py +91 -0
- fi/alk/harness/world/handle.py +538 -0
- fi/alk/harness/world/kinds.py +196 -0
- fi/alk/harness/world/mutate.py +186 -0
- fi/alk/harness/world/probe.py +413 -0
- fi/alk/harness/world/provision.py +511 -0
- fi/alk/harness/world/provisioned.py +191 -0
- fi/alk/harness/world/runtime.py +616 -0
- fi/alk/harness/world/snapshot.py +288 -0
- fi/alk/harness/world/stores/__init__.py +305 -0
- fi/alk/harness/world/stores/container.py +215 -0
- fi/alk/harness/world/stores/inprocess.py +346 -0
- fi/alk/harness/world/stores/postgres.py +481 -0
- fi/alk/harness/world/stores/prove.py +202 -0
- fi/alk/harness/world/stores/sqlite.py +245 -0
- fi/alk/harness/world/stores/written.py +182 -0
- fi/alk/harness/world/tools.py +1516 -0
- fi/alk/harness/world/workspace.py +144 -0
- fi/alk/image_loop.py +453 -0
- fi/alk/image_perturb.py +241 -0
- fi/alk/improve.py +274 -0
- fi/alk/live/__init__.py +154 -0
- fi/alk/live/_attribution.py +184 -0
- fi/alk/live/_capture.py +264 -0
- fi/alk/live/_codec.py +391 -0
- fi/alk/live/_contract.py +134 -0
- fi/alk/live/_loopback.py +316 -0
- fi/alk/live/_perturb.py +449 -0
- fi/alk/live/_runner.py +386 -0
- fi/alk/live/_stats.py +561 -0
- fi/alk/live/_transcript.py +240 -0
- fi/alk/live/_workers/__init__.py +9 -0
- fi/alk/live/_workers/a2a_worker.py +316 -0
- fi/alk/live/_workers/langgraph_worker.py +217 -0
- fi/alk/live/_workers/livekit_worker.py +207 -0
- fi/alk/live/_workers/mcp_loopback_server.py +46 -0
- fi/alk/live/_workers/mcp_worker.py +158 -0
- fi/alk/live/_workers/pipecat_worker.py +189 -0
- fi/alk/live/a2a_lane.py +138 -0
- fi/alk/live/langgraph_lane.py +339 -0
- fi/alk/live/livekit_lane.py +376 -0
- fi/alk/live/mcp_lane.py +172 -0
- fi/alk/live/pipecat_lane.py +341 -0
- fi/alk/live/voice_redteam.py +494 -0
- fi/alk/loss.py +306 -0
- fi/alk/optimize.py +36260 -0
- fi/alk/practice/__init__.py +51 -0
- fi/alk/practice/_assess.py +103 -0
- fi/alk/practice/_budget.py +81 -0
- fi/alk/practice/_calibrate.py +69 -0
- fi/alk/practice/_capstone.py +86 -0
- fi/alk/practice/_contract.py +91 -0
- fi/alk/practice/_diagnose.py +79 -0
- fi/alk/practice/_drill.py +196 -0
- fi/alk/practice/_experiment.py +720 -0
- fi/alk/practice/_schedule.py +102 -0
- fi/alk/practice/_store.py +194 -0
- fi/alk/practice/_trainer.py +245 -0
- fi/alk/practice/_update.py +125 -0
- fi/alk/redteam.py +2621 -0
- fi/alk/rewardhack.py +237 -0
- fi/alk/simulate.py +10351 -0
- fi/alk/studio/__init__.py +82 -0
- fi/alk/studio/_bias.py +314 -0
- fi/alk/studio/_calibration.py +522 -0
- fi/alk/studio/_coverage.py +262 -0
- fi/alk/studio/_download.py +665 -0
- fi/alk/studio/_fidelity_attack.py +114 -0
- fi/alk/studio/_generate.py +652 -0
- fi/alk/studio/_library.py +370 -0
- fi/alk/studio/_scan.py +134 -0
- fi/alk/studio/_upgrade.py +42 -0
- fi/alk/studio/_vendor.py +172 -0
- fi/alk/suite.py +4200 -0
- fi/alk/tasks.py +828 -0
- fi/alk/telemetry/__init__.py +149 -0
- fi/alk/telemetry/_contract.py +141 -0
- fi/alk/telemetry/_emit.py +182 -0
- fi/alk/telemetry/_ledger.py +296 -0
- fi/alk/telemetry/_queue.py +127 -0
- fi/alk/telemetry/_row.py +294 -0
- fi/alk/telemetry/_run.py +233 -0
- fi/alk/telemetry/_sync.py +193 -0
- fi/alk/telemetry/_url.py +119 -0
- fi/alk/trinity.py +49397 -0
- fi/alk/voice_loop.py +174 -0
- fi/api/__init__.py +1 -0
- fi/api/auth.py +137 -0
- fi/api/types.py +29 -0
- fi/cli/__init__.py +9 -0
- fi/cli/assertions/__init__.py +25 -0
- fi/cli/assertions/conditions.py +76 -0
- fi/cli/assertions/evaluator.py +286 -0
- fi/cli/assertions/exit_codes.py +20 -0
- fi/cli/assertions/parser.py +131 -0
- fi/cli/assertions/reporter.py +194 -0
- fi/cli/commands/__init__.py +9 -0
- fi/cli/commands/config.py +165 -0
- fi/cli/commands/export.py +208 -0
- fi/cli/commands/init.py +112 -0
- fi/cli/commands/list_cmd.py +213 -0
- fi/cli/commands/run.py +486 -0
- fi/cli/commands/validate.py +173 -0
- fi/cli/commands/view.py +424 -0
- fi/cli/config/__init__.py +6 -0
- fi/cli/config/defaults.py +206 -0
- fi/cli/config/loader.py +155 -0
- fi/cli/config/schema.py +174 -0
- fi/cli/main.py +78 -0
- fi/cli/output/__init__.py +6 -0
- fi/cli/output/formatters.py +106 -0
- fi/cli/output/reporters.py +46 -0
- fi/cli/storage/__init__.py +5 -0
- fi/cli/storage/run_history.py +249 -0
- fi/cli/utils/__init__.py +5 -0
- fi/cli/utils/console.py +44 -0
- fi/evals/__init__.py +131 -0
- fi/evals/autoeval/__init__.py +137 -0
- fi/evals/autoeval/analyzer.py +211 -0
- fi/evals/autoeval/config.py +244 -0
- fi/evals/autoeval/export.py +213 -0
- fi/evals/autoeval/interactive.py +283 -0
- fi/evals/autoeval/pipeline.py +625 -0
- fi/evals/autoeval/prompts.py +139 -0
- fi/evals/autoeval/recommender.py +242 -0
- fi/evals/autoeval/rules.py +589 -0
- fi/evals/autoeval/templates.py +299 -0
- fi/evals/autoeval/types.py +232 -0
- fi/evals/core/__init__.py +16 -0
- fi/evals/core/cloud_registry.py +184 -0
- fi/evals/core/engines.py +368 -0
- fi/evals/core/evaluate.py +319 -0
- fi/evals/core/judge_prompt.py +90 -0
- fi/evals/core/prompt_generator.py +83 -0
- fi/evals/core/registry.py +57 -0
- fi/evals/core/result.py +55 -0
- fi/evals/evaluator.py +721 -0
- fi/evals/execution.py +168 -0
- fi/evals/feedback/__init__.py +32 -0
- fi/evals/feedback/calibrator.py +160 -0
- fi/evals/feedback/collector.py +214 -0
- fi/evals/feedback/hooks.py +81 -0
- fi/evals/feedback/retriever.py +128 -0
- fi/evals/feedback/store.py +272 -0
- fi/evals/feedback/types.py +99 -0
- fi/evals/framework/README.md +79 -0
- fi/evals/framework/__init__.py +267 -0
- fi/evals/framework/backends/Dockerfile.eval-runner +33 -0
- fi/evals/framework/backends/__init__.py +99 -0
- fi/evals/framework/backends/_container.py +141 -0
- fi/evals/framework/backends/_utils.py +145 -0
- fi/evals/framework/backends/base.py +223 -0
- fi/evals/framework/backends/celery_backend.py +417 -0
- fi/evals/framework/backends/celery_worker.py +78 -0
- fi/evals/framework/backends/kubernetes_backend.py +665 -0
- fi/evals/framework/backends/ray_backend.py +521 -0
- fi/evals/framework/backends/temporal.py +350 -0
- fi/evals/framework/backends/temporal_worker.py +126 -0
- fi/evals/framework/backends/thread_pool.py +286 -0
- fi/evals/framework/context.py +258 -0
- fi/evals/framework/enrichment.py +306 -0
- fi/evals/framework/evals/__init__.py +68 -0
- fi/evals/framework/evals/agentic.py +399 -0
- fi/evals/framework/evals/builder.py +609 -0
- fi/evals/framework/evals/semantic.py +142 -0
- fi/evals/framework/evaluator.py +647 -0
- fi/evals/framework/evaluators/__init__.py +22 -0
- fi/evals/framework/evaluators/blocking.py +347 -0
- fi/evals/framework/evaluators/non_blocking.py +577 -0
- fi/evals/framework/propagation.py +421 -0
- fi/evals/framework/protocols.py +385 -0
- fi/evals/framework/registry.py +370 -0
- fi/evals/framework/resilience/__init__.py +150 -0
- fi/evals/framework/resilience/circuit_breaker.py +309 -0
- fi/evals/framework/resilience/degradation.py +355 -0
- fi/evals/framework/resilience/health.py +505 -0
- fi/evals/framework/resilience/rate_limiter.py +228 -0
- fi/evals/framework/resilience/retry.py +274 -0
- fi/evals/framework/resilience/types.py +288 -0
- fi/evals/framework/resilience/wrapper.py +433 -0
- fi/evals/framework/types.py +218 -0
- fi/evals/guardrails/README.md +915 -0
- fi/evals/guardrails/__init__.py +96 -0
- fi/evals/guardrails/backends/__init__.py +43 -0
- fi/evals/guardrails/backends/azure.py +361 -0
- fi/evals/guardrails/backends/base.py +88 -0
- fi/evals/guardrails/backends/generic_llm.py +163 -0
- fi/evals/guardrails/backends/granite.py +216 -0
- fi/evals/guardrails/backends/llamaguard.py +221 -0
- fi/evals/guardrails/backends/local_base.py +479 -0
- fi/evals/guardrails/backends/openai.py +365 -0
- fi/evals/guardrails/backends/qwen.py +170 -0
- fi/evals/guardrails/backends/shieldgemma.py +154 -0
- fi/evals/guardrails/backends/turing.py +235 -0
- fi/evals/guardrails/backends/vllm_client.py +321 -0
- fi/evals/guardrails/backends/wildguard.py +188 -0
- fi/evals/guardrails/base.py +888 -0
- fi/evals/guardrails/config.py +221 -0
- fi/evals/guardrails/discovery.py +243 -0
- fi/evals/guardrails/gateway.py +437 -0
- fi/evals/guardrails/registry.py +231 -0
- fi/evals/guardrails/scanners/__init__.py +127 -0
- fi/evals/guardrails/scanners/base.py +191 -0
- fi/evals/guardrails/scanners/code_injection.py +243 -0
- fi/evals/guardrails/scanners/eval_delegate.py +574 -0
- fi/evals/guardrails/scanners/invisible_chars.py +351 -0
- fi/evals/guardrails/scanners/jailbreak.py +412 -0
- fi/evals/guardrails/scanners/language.py +288 -0
- fi/evals/guardrails/scanners/pipeline.py +260 -0
- fi/evals/guardrails/scanners/regex.py +311 -0
- fi/evals/guardrails/scanners/secrets.py +274 -0
- fi/evals/guardrails/scanners/topics.py +649 -0
- fi/evals/guardrails/scanners/urls.py +341 -0
- fi/evals/guardrails/types.py +96 -0
- fi/evals/llm/__init__.py +3 -0
- fi/evals/llm/base_llm_provider.py +35 -0
- fi/evals/llm/providers/litellm.py +70 -0
- fi/evals/local/__init__.py +90 -0
- fi/evals/local/evaluator.py +690 -0
- fi/evals/local/execution_mode.py +121 -0
- fi/evals/local/llm.py +489 -0
- fi/evals/local/metrics/__init__.py +19 -0
- fi/evals/local/registry.py +360 -0
- fi/evals/manager.py +1018 -0
- fi/evals/manager_types.py +362 -0
- fi/evals/metrics/__init__.py +185 -0
- fi/evals/metrics/agents/__init__.py +74 -0
- fi/evals/metrics/agents/metrics.py +693 -0
- fi/evals/metrics/agents/report.py +36463 -0
- fi/evals/metrics/agents/types.py +160 -0
- fi/evals/metrics/base_llm_metric.py +111 -0
- fi/evals/metrics/base_metric.py +138 -0
- fi/evals/metrics/code_security/__init__.py +305 -0
- fi/evals/metrics/code_security/analyzer.py +985 -0
- fi/evals/metrics/code_security/benchmarks/__init__.py +73 -0
- fi/evals/metrics/code_security/benchmarks/builtin.py +750 -0
- fi/evals/metrics/code_security/benchmarks/loader.py +580 -0
- fi/evals/metrics/code_security/benchmarks/types.py +308 -0
- fi/evals/metrics/code_security/detectors/__init__.py +186 -0
- fi/evals/metrics/code_security/detectors/base.py +394 -0
- fi/evals/metrics/code_security/detectors/cryptography.py +345 -0
- fi/evals/metrics/code_security/detectors/injection.py +744 -0
- fi/evals/metrics/code_security/detectors/secrets.py +287 -0
- fi/evals/metrics/code_security/detectors/serialization.py +192 -0
- fi/evals/metrics/code_security/joint_metrics.py +588 -0
- fi/evals/metrics/code_security/judges/__init__.py +83 -0
- fi/evals/metrics/code_security/judges/base.py +238 -0
- fi/evals/metrics/code_security/judges/dual_judge.py +534 -0
- fi/evals/metrics/code_security/judges/llm_judge.py +301 -0
- fi/evals/metrics/code_security/judges/pattern_judge.py +515 -0
- fi/evals/metrics/code_security/metrics.py +388 -0
- fi/evals/metrics/code_security/modes/__init__.py +63 -0
- fi/evals/metrics/code_security/modes/adversarial.py +284 -0
- fi/evals/metrics/code_security/modes/autocomplete.py +198 -0
- fi/evals/metrics/code_security/modes/base.py +283 -0
- fi/evals/metrics/code_security/modes/instruct.py +253 -0
- fi/evals/metrics/code_security/modes/repair.py +230 -0
- fi/evals/metrics/code_security/reports/__init__.py +57 -0
- fi/evals/metrics/code_security/reports/generator.py +404 -0
- fi/evals/metrics/code_security/reports/leaderboard.py +509 -0
- fi/evals/metrics/code_security/types.py +534 -0
- fi/evals/metrics/function_calling/__init__.py +34 -0
- fi/evals/metrics/function_calling/metrics.py +573 -0
- fi/evals/metrics/function_calling/types.py +87 -0
- fi/evals/metrics/hallucination/__init__.py +54 -0
- fi/evals/metrics/hallucination/detector.py +149 -0
- fi/evals/metrics/hallucination/metrics.py +390 -0
- fi/evals/metrics/hallucination/nli.py +253 -0
- fi/evals/metrics/hallucination/sentinel.py +106 -0
- fi/evals/metrics/hallucination/types.py +132 -0
- fi/evals/metrics/heuristics/aggregation_metrics.py +85 -0
- fi/evals/metrics/heuristics/json_metrics.py +87 -0
- fi/evals/metrics/heuristics/similarity_metrics.py +375 -0
- fi/evals/metrics/heuristics/string_metrics.py +391 -0
- fi/evals/metrics/llm_as_judges/__init__.py +17 -0
- fi/evals/metrics/llm_as_judges/custom_judge/metric.py +112 -0
- fi/evals/metrics/llm_as_judges/custom_judge/prompts.py +26 -0
- fi/evals/metrics/llm_as_judges/types.py +48 -0
- fi/evals/metrics/rag/__init__.py +111 -0
- fi/evals/metrics/rag/advanced/__init__.py +14 -0
- fi/evals/metrics/rag/advanced/multi_hop.py +283 -0
- fi/evals/metrics/rag/advanced/source_attribution.py +344 -0
- fi/evals/metrics/rag/generation/__init__.py +17 -0
- fi/evals/metrics/rag/generation/answer_relevancy.py +176 -0
- fi/evals/metrics/rag/generation/context_utilization.py +245 -0
- fi/evals/metrics/rag/generation/faithfulness.py +241 -0
- fi/evals/metrics/rag/generation/groundedness.py +131 -0
- fi/evals/metrics/rag/rag_score.py +277 -0
- fi/evals/metrics/rag/retrieval/__init__.py +20 -0
- fi/evals/metrics/rag/retrieval/context_entity_recall.py +124 -0
- fi/evals/metrics/rag/retrieval/context_precision.py +158 -0
- fi/evals/metrics/rag/retrieval/context_recall.py +106 -0
- fi/evals/metrics/rag/retrieval/noise_sensitivity.py +163 -0
- fi/evals/metrics/rag/retrieval/ranking.py +261 -0
- fi/evals/metrics/rag/types.py +100 -0
- fi/evals/metrics/rag/utils/__init__.py +62 -0
- fi/evals/metrics/rag/utils/claims.py +189 -0
- fi/evals/metrics/rag/utils/entities.py +244 -0
- fi/evals/metrics/rag/utils/nli.py +92 -0
- fi/evals/metrics/rag/utils/similarity.py +345 -0
- fi/evals/metrics/structured/__init__.py +114 -0
- fi/evals/metrics/structured/field_completeness.py +313 -0
- fi/evals/metrics/structured/hierarchy_score.py +366 -0
- fi/evals/metrics/structured/json_validation.py +190 -0
- fi/evals/metrics/structured/schema_compliance.py +280 -0
- fi/evals/metrics/structured/structured_output_score.py +298 -0
- fi/evals/metrics/structured/types.py +108 -0
- fi/evals/metrics/structured/validators/__init__.py +30 -0
- fi/evals/metrics/structured/validators/base.py +189 -0
- fi/evals/metrics/structured/validators/json_validator.py +196 -0
- fi/evals/metrics/structured/validators/pydantic_validator.py +178 -0
- fi/evals/metrics/structured/validators/yaml_validator.py +248 -0
- fi/evals/otel/__init__.py +266 -0
- fi/evals/otel/config.py +400 -0
- fi/evals/otel/conventions.py +463 -0
- fi/evals/otel/enrichment.py +371 -0
- fi/evals/otel/instrumentors/__init__.py +140 -0
- fi/evals/otel/instrumentors/anthropic.py +517 -0
- fi/evals/otel/instrumentors/base.py +382 -0
- fi/evals/otel/instrumentors/openai.py +673 -0
- fi/evals/otel/processors/__init__.py +36 -0
- fi/evals/otel/processors/base.py +473 -0
- fi/evals/otel/processors/cost.py +445 -0
- fi/evals/otel/processors/evaluation.py +559 -0
- fi/evals/otel/processors/llm.py +462 -0
- fi/evals/otel/tracer.py +506 -0
- fi/evals/otel/types.py +232 -0
- fi/evals/otel_utils.py +23 -0
- fi/evals/protect.py +671 -0
- fi/evals/protect_input_adapter.py +154 -0
- fi/evals/streaming/__init__.py +88 -0
- fi/evals/streaming/buffer.py +213 -0
- fi/evals/streaming/evaluator.py +551 -0
- fi/evals/streaming/policy.py +307 -0
- fi/evals/streaming/scorers.py +368 -0
- fi/evals/streaming/types.py +238 -0
- fi/evals/templates.py +472 -0
- fi/evals/types.py +156 -0
- fi/opt/__init__.py +221 -0
- fi/opt/_objective_scoring.py +85 -0
- fi/opt/base/__init__.py +11 -0
- fi/opt/base/base_generator.py +33 -0
- fi/opt/base/base_mapper.py +26 -0
- fi/opt/base/base_optimizer.py +45 -0
- fi/opt/base/evaluator.py +211 -0
- fi/opt/components.py +3095 -0
- fi/opt/datamappers/__init__.py +3 -0
- fi/opt/datamappers/basic_mapper.py +40 -0
- fi/opt/deployment.py +1021 -0
- fi/opt/evidence.py +4332 -0
- fi/opt/generators/__init__.py +3 -0
- fi/opt/generators/litellm.py +66 -0
- fi/opt/integrations/__init__.py +23 -0
- fi/opt/integrations/generative_suite.py +410 -0
- fi/opt/integrations/simulate.py +1313 -0
- fi/opt/mutations.py +771 -0
- fi/opt/observability.py +4639 -0
- fi/opt/optimizer_trace.py +889 -0
- fi/opt/optimizers/__init__.py +80 -0
- fi/opt/optimizers/agent.py +331 -0
- fi/opt/optimizers/agent_bandit.py +392 -0
- fi/opt/optimizers/agent_curriculum.py +635 -0
- fi/opt/optimizers/agent_evolution.py +894 -0
- fi/opt/optimizers/agent_feedback.py +1863 -0
- fi/opt/optimizers/agent_pareto.py +547 -0
- fi/opt/optimizers/agent_social_memory.py +1113 -0
- fi/opt/optimizers/agent_tpe.py +321 -0
- fi/opt/optimizers/bayesian_search.py +449 -0
- fi/opt/optimizers/council.py +2075 -0
- fi/opt/optimizers/futureagi_replay.py +799 -0
- fi/opt/optimizers/gepa.py +322 -0
- fi/opt/optimizers/metaprompt.py +243 -0
- fi/opt/optimizers/promptwizard.py +417 -0
- fi/opt/optimizers/protegi.py +329 -0
- fi/opt/optimizers/random_search.py +224 -0
- fi/opt/research.py +518 -0
- fi/opt/simulation.py +260 -0
- fi/opt/targets.py +232 -0
- fi/opt/types.py +66 -0
- fi/opt/utils/__init__.py +4 -0
- fi/opt/utils/early_stopping.py +266 -0
- fi/opt/utils/setup_logging.py +82 -0
- fi/simulate/__init__.py +540 -0
- fi/simulate/_hashing.py +35 -0
- fi/simulate/_logging.py +10 -0
- fi/simulate/adapters.py +87 -0
- fi/simulate/agent/__init__.py +120 -0
- fi/simulate/agent/browser.py +658 -0
- fi/simulate/agent/definition.py +587 -0
- fi/simulate/agent/frameworks.py +3528 -0
- fi/simulate/agent/generic.py +8286 -0
- fi/simulate/agent/import_probe.py +227 -0
- fi/simulate/agent/memory.py +905 -0
- fi/simulate/agent/mocks.py +101 -0
- fi/simulate/agent/multi_agent.py +361 -0
- fi/simulate/agent/orchestration.py +903 -0
- fi/simulate/agent/realtime.py +665 -0
- fi/simulate/agent/wrapper.py +99 -0
- fi/simulate/agent/wrappers/__init__.py +18 -0
- fi/simulate/agent/wrappers/anthropic.py +62 -0
- fi/simulate/agent/wrappers/gemini.py +65 -0
- fi/simulate/agent/wrappers/http.py +404 -0
- fi/simulate/agent/wrappers/langchain.py +80 -0
- fi/simulate/agent/wrappers/openai.py +75 -0
- fi/simulate/agent/wrappers/websocket.py +326 -0
- fi/simulate/artifacts/__init__.py +11 -0
- fi/simulate/artifacts/manifest.py +62 -0
- fi/simulate/cli.py +20560 -0
- fi/simulate/endpoints/__init__.py +45 -0
- fi/simulate/endpoints/_http_actor.py +73 -0
- fi/simulate/endpoints/actor_sources.py +243 -0
- fi/simulate/endpoints/base.py +107 -0
- fi/simulate/endpoints/builtins.py +10 -0
- fi/simulate/endpoints/callable.py +95 -0
- fi/simulate/endpoints/http.py +76 -0
- fi/simulate/endpoints/livekit.py +138 -0
- fi/simulate/endpoints/originators.py +132 -0
- fi/simulate/endpoints/profiles.py +348 -0
- fi/simulate/endpoints/retell.py +633 -0
- fi/simulate/endpoints/vapi.py +205 -0
- fi/simulate/endpoints/websocket.py +76 -0
- fi/simulate/environment.py +33026 -0
- fi/simulate/environments/__init__.py +11 -0
- fi/simulate/environments/base.py +73 -0
- fi/simulate/environments/chat.py +697 -0
- fi/simulate/environments/voice.py +212 -0
- fi/simulate/evaluation/__init__.py +4 -0
- fi/simulate/evaluation/ai_eval.py +227 -0
- fi/simulate/evidence/__init__.py +35 -0
- fi/simulate/evidence/base.py +59 -0
- fi/simulate/evidence/caller_observed.py +50 -0
- fi/simulate/evidence/livekit_instrumentation.py +51 -0
- fi/simulate/evidence/livekit_room.py +50 -0
- fi/simulate/evidence/otel.py +49 -0
- fi/simulate/evidence/providers/__init__.py +24 -0
- fi/simulate/evidence/providers/base.py +61 -0
- fi/simulate/evidence/providers/retell.py +376 -0
- fi/simulate/evidence/providers/vapi.py +426 -0
- fi/simulate/hosted/__init__.py +32 -0
- fi/simulate/hosted/child_entrypoint.py +306 -0
- fi/simulate/hosted/job.py +150 -0
- fi/simulate/hosted/targets.py +53 -0
- fi/simulate/instrumentation/__init__.py +5 -0
- fi/simulate/instrumentation/livekit/__init__.py +122 -0
- fi/simulate/manifest.py +1033 -0
- fi/simulate/matrix_cli.py +165 -0
- fi/simulate/realtime/__init__.py +40 -0
- fi/simulate/realtime/events.py +107 -0
- fi/simulate/realtime/media.py +61 -0
- fi/simulate/realtime/session.py +91 -0
- fi/simulate/recording/__init__.py +5 -0
- fi/simulate/recording/room_recorder.py +326 -0
- fi/simulate/registry.py +185 -0
- fi/simulate/results/__init__.py +9 -0
- fi/simulate/results/base.py +18 -0
- fi/simulate/results/filesystem.py +71 -0
- fi/simulate/results/futureagi.py +1340 -0
- fi/simulate/runtime/__init__.py +85 -0
- fi/simulate/runtime/capabilities.py +40 -0
- fi/simulate/runtime/events.py +63 -0
- fi/simulate/runtime/failures.py +25 -0
- fi/simulate/runtime/ids.py +34 -0
- fi/simulate/runtime/plan.py +70 -0
- fi/simulate/runtime/planner.py +102 -0
- fi/simulate/runtime/report.py +174 -0
- fi/simulate/runtime/run.py +75 -0
- fi/simulate/runtime/runner.py +333 -0
- fi/simulate/runtime/spec.py +186 -0
- fi/simulate/simulation/__init__.py +30 -0
- fi/simulate/simulation/behavior_policy.py +425 -0
- fi/simulate/simulation/bridge/__init__.py +9 -0
- fi/simulate/simulation/bridge/audio.py +29 -0
- fi/simulate/simulation/bridge/connector.py +46 -0
- fi/simulate/simulation/bridge/livekit.py +252 -0
- fi/simulate/simulation/bridge/retell.py +188 -0
- fi/simulate/simulation/bridge/vapi.py +177 -0
- fi/simulate/simulation/contract.py +419 -0
- fi/simulate/simulation/engines/__init__.py +12 -0
- fi/simulate/simulation/engines/base.py +21 -0
- fi/simulate/simulation/engines/cloud.py +517 -0
- fi/simulate/simulation/engines/livekit.py +4167 -0
- fi/simulate/simulation/engines/local_text.py +89 -0
- fi/simulate/simulation/fidelity.py +374 -0
- fi/simulate/simulation/gemini_tts_stream.py +110 -0
- fi/simulate/simulation/generator.py +91 -0
- fi/simulate/simulation/goal_machine.py +185 -0
- fi/simulate/simulation/livekit_models.py +467 -0
- fi/simulate/simulation/matrix.py +170 -0
- fi/simulate/simulation/models.py +279 -0
- fi/simulate/simulation/runner.py +153 -0
- fi/simulate/simulation/synthetic.py +880 -0
- fi/simulate/simulation/voice_prompt.py +502 -0
- fi/simulate/simulator/__init__.py +55 -0
- fi/simulate/simulator/builtins.py +53 -0
- fi/simulate/suite.py +1288 -0
- fi/simulate/utils/routes.py +164 -0
- fi/simulate/voice.py +225 -0
- fi/simulate/voice_cli.py +182 -0
- fi/utils/__init__.py +1 -0
- fi/utils/constants.py +14 -0
- fi/utils/errors.py +200 -0
- fi/utils/executor.py +26 -0
- fi/utils/routes.py +119 -0
- 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
|
+
]
|