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,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claim extraction utilities for RAG evaluation.
|
|
3
|
+
|
|
4
|
+
Provides functions to split text into sentences and extract
|
|
5
|
+
verifiable claims from responses.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import List, Set
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def split_into_sentences(text: str) -> List[str]:
|
|
13
|
+
"""
|
|
14
|
+
Split text into sentences.
|
|
15
|
+
|
|
16
|
+
Uses regex-based sentence boundary detection.
|
|
17
|
+
Handles common abbreviations and edge cases.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
text: The text to split
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
List of sentence strings
|
|
24
|
+
"""
|
|
25
|
+
if not text or not text.strip():
|
|
26
|
+
return []
|
|
27
|
+
|
|
28
|
+
# Handle common abbreviations
|
|
29
|
+
text = re.sub(r"\b(Mr|Mrs|Ms|Dr|Prof|Sr|Jr|etc|vs|i\.e|e\.g)\.", r"\1<DOT>", text)
|
|
30
|
+
|
|
31
|
+
# Split on sentence boundaries
|
|
32
|
+
sentences = re.split(r"(?<=[.!?])\s+", text.strip())
|
|
33
|
+
|
|
34
|
+
# Restore abbreviations
|
|
35
|
+
sentences = [s.replace("<DOT>", ".").strip() for s in sentences if s.strip()]
|
|
36
|
+
|
|
37
|
+
return sentences
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def extract_claims(text: str, min_words: int = 3) -> List[str]:
|
|
41
|
+
"""
|
|
42
|
+
Extract verifiable claims from text.
|
|
43
|
+
|
|
44
|
+
Filters out questions, meta-statements, and very short sentences.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
text: The text to extract claims from
|
|
48
|
+
min_words: Minimum number of words for a valid claim
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of claim strings
|
|
52
|
+
"""
|
|
53
|
+
sentences = split_into_sentences(text)
|
|
54
|
+
claims = []
|
|
55
|
+
|
|
56
|
+
for sentence in sentences:
|
|
57
|
+
# Skip very short sentences
|
|
58
|
+
if len(sentence.split()) < min_words:
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
# Skip questions
|
|
62
|
+
if sentence.strip().endswith("?"):
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# Skip meta-statements and hedging
|
|
66
|
+
meta_patterns = [
|
|
67
|
+
r"^(I think|I believe|In my opinion|It seems|Perhaps|Maybe)",
|
|
68
|
+
r"^(Here is|Here are|The following|Below is|Above is)",
|
|
69
|
+
r"^(Note:|Important:|Warning:|Disclaimer:|Please note)",
|
|
70
|
+
r"^(Let me|I will|I can|I would|I should)",
|
|
71
|
+
r"^(This is|That is|These are|Those are)\s+(a|an|the)\s+(question|query|request)",
|
|
72
|
+
]
|
|
73
|
+
is_meta = any(re.match(p, sentence, re.IGNORECASE) for p in meta_patterns)
|
|
74
|
+
if is_meta:
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# Skip acknowledgments and greetings
|
|
78
|
+
ack_patterns = [
|
|
79
|
+
r"^(Thank you|Thanks|Sure|Of course|Certainly|Absolutely)",
|
|
80
|
+
r"^(Hello|Hi|Hey|Good morning|Good afternoon|Good evening)",
|
|
81
|
+
]
|
|
82
|
+
is_ack = any(re.match(p, sentence, re.IGNORECASE) for p in ack_patterns)
|
|
83
|
+
if is_ack:
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
claims.append(sentence)
|
|
87
|
+
|
|
88
|
+
return claims
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def extract_key_phrases(text: str, max_phrases: int = 20) -> List[str]:
|
|
92
|
+
"""
|
|
93
|
+
Extract key phrases from text.
|
|
94
|
+
|
|
95
|
+
Uses heuristics to identify important noun phrases and
|
|
96
|
+
subject-verb-object patterns.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
text: The text to extract phrases from
|
|
100
|
+
max_phrases: Maximum number of phrases to return
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
List of key phrase strings
|
|
104
|
+
"""
|
|
105
|
+
phrases = set()
|
|
106
|
+
|
|
107
|
+
# Extract capitalized phrases (proper nouns)
|
|
108
|
+
proper_nouns = re.findall(r"\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b", text)
|
|
109
|
+
phrases.update(proper_nouns)
|
|
110
|
+
|
|
111
|
+
# Extract quoted phrases
|
|
112
|
+
quoted = re.findall(r'"([^"]+)"', text)
|
|
113
|
+
phrases.update(quoted)
|
|
114
|
+
|
|
115
|
+
# Extract phrases with numbers
|
|
116
|
+
numbered = re.findall(
|
|
117
|
+
r"\b\d+(?:\.\d+)?(?:\s+(?:percent|%|million|billion|thousand|hundred|dollars?|euros?|pounds?|years?|months?|days?|hours?|minutes?|seconds?))\b",
|
|
118
|
+
text,
|
|
119
|
+
re.IGNORECASE,
|
|
120
|
+
)
|
|
121
|
+
phrases.update(numbered)
|
|
122
|
+
|
|
123
|
+
# Extract noun phrases using simple patterns
|
|
124
|
+
# Pattern: adjective(s) + noun(s)
|
|
125
|
+
noun_phrases = re.findall(
|
|
126
|
+
r"\b(?:the\s+)?(?:[A-Za-z]+\s+){0,2}[A-Za-z]+(?:tion|ment|ness|ity|ence|ance|ing|ed)\b",
|
|
127
|
+
text,
|
|
128
|
+
)
|
|
129
|
+
phrases.update(noun_phrases[:10])
|
|
130
|
+
|
|
131
|
+
# Filter and limit
|
|
132
|
+
filtered = [p for p in phrases if len(p) > 2 and len(p.split()) <= 5]
|
|
133
|
+
|
|
134
|
+
return filtered[:max_phrases]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def extract_atomic_claims(text: str) -> List[str]:
|
|
138
|
+
"""
|
|
139
|
+
Extract atomic (indivisible) claims from text.
|
|
140
|
+
|
|
141
|
+
Breaks down compound sentences into simpler claims.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
text: The text to extract atomic claims from
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
List of atomic claim strings
|
|
148
|
+
"""
|
|
149
|
+
claims = extract_claims(text)
|
|
150
|
+
atomic_claims = []
|
|
151
|
+
|
|
152
|
+
for claim in claims:
|
|
153
|
+
# Split compound sentences on conjunctions
|
|
154
|
+
parts = re.split(r"\b(?:and|but|however|although|while|whereas)\b", claim)
|
|
155
|
+
|
|
156
|
+
for part in parts:
|
|
157
|
+
part = part.strip()
|
|
158
|
+
# Clean up leading/trailing punctuation
|
|
159
|
+
part = re.sub(r"^[,;:\s]+|[,;:\s]+$", "", part)
|
|
160
|
+
|
|
161
|
+
if len(part.split()) >= 3:
|
|
162
|
+
atomic_claims.append(part)
|
|
163
|
+
|
|
164
|
+
return atomic_claims
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def get_claim_entities(claim: str) -> Set[str]:
|
|
168
|
+
"""
|
|
169
|
+
Extract entity-like tokens from a claim.
|
|
170
|
+
|
|
171
|
+
Simple extraction without requiring NER models.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
claim: The claim text
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Set of entity-like strings
|
|
178
|
+
"""
|
|
179
|
+
entities = set()
|
|
180
|
+
|
|
181
|
+
# Capitalized words (likely proper nouns)
|
|
182
|
+
caps = re.findall(r"\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b", claim)
|
|
183
|
+
entities.update(caps)
|
|
184
|
+
|
|
185
|
+
# Numbers with context
|
|
186
|
+
numbers = re.findall(r"\b\d+(?:\.\d+)?(?:\s+\w+)?\b", claim)
|
|
187
|
+
entities.update(numbers)
|
|
188
|
+
|
|
189
|
+
return entities
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entity extraction utilities for RAG evaluation.
|
|
3
|
+
|
|
4
|
+
Provides functions to extract named entities from text.
|
|
5
|
+
Supports spaCy NER with heuristic fallback.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import List, Optional, Set
|
|
10
|
+
|
|
11
|
+
# Optional spaCy import
|
|
12
|
+
_SPACY_AVAILABLE = False
|
|
13
|
+
_nlp = None
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import spacy
|
|
17
|
+
|
|
18
|
+
_SPACY_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
spacy = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_spacy_model():
|
|
24
|
+
"""Lazy-load spaCy model."""
|
|
25
|
+
global _nlp
|
|
26
|
+
if _nlp is None and _SPACY_AVAILABLE:
|
|
27
|
+
try:
|
|
28
|
+
_nlp = spacy.load("en_core_web_sm")
|
|
29
|
+
except OSError:
|
|
30
|
+
# Model not downloaded, use fallback
|
|
31
|
+
_nlp = False
|
|
32
|
+
return _nlp
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def extract_entities_spacy(
|
|
36
|
+
text: str, entity_types: Optional[List[str]] = None
|
|
37
|
+
) -> Set[str]:
|
|
38
|
+
"""
|
|
39
|
+
Extract entities using spaCy NER.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
text: The text to extract entities from
|
|
43
|
+
entity_types: Optional list of entity types to filter by
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Set of entity strings
|
|
47
|
+
"""
|
|
48
|
+
nlp = _get_spacy_model()
|
|
49
|
+
if not nlp:
|
|
50
|
+
return extract_entities_heuristic(text)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
doc = nlp(text)
|
|
54
|
+
entities = set()
|
|
55
|
+
|
|
56
|
+
# Default entity types of interest
|
|
57
|
+
default_types = {
|
|
58
|
+
"PERSON",
|
|
59
|
+
"ORG",
|
|
60
|
+
"GPE",
|
|
61
|
+
"LOC",
|
|
62
|
+
"DATE",
|
|
63
|
+
"TIME",
|
|
64
|
+
"MONEY",
|
|
65
|
+
"QUANTITY",
|
|
66
|
+
"PRODUCT",
|
|
67
|
+
"EVENT",
|
|
68
|
+
"WORK_OF_ART",
|
|
69
|
+
"LAW",
|
|
70
|
+
"LANGUAGE",
|
|
71
|
+
"PERCENT",
|
|
72
|
+
"CARDINAL",
|
|
73
|
+
"ORDINAL",
|
|
74
|
+
}
|
|
75
|
+
target_types = set(entity_types) if entity_types else default_types
|
|
76
|
+
|
|
77
|
+
for ent in doc.ents:
|
|
78
|
+
if ent.label_ in target_types:
|
|
79
|
+
entities.add(ent.text)
|
|
80
|
+
|
|
81
|
+
return entities
|
|
82
|
+
except Exception:
|
|
83
|
+
return extract_entities_heuristic(text)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def extract_entities_heuristic(text: str) -> Set[str]:
|
|
87
|
+
"""
|
|
88
|
+
Fallback entity extraction using heuristics.
|
|
89
|
+
|
|
90
|
+
Extracts:
|
|
91
|
+
- Capitalized phrases (likely proper nouns)
|
|
92
|
+
- Numbers with units
|
|
93
|
+
- Dates in common formats
|
|
94
|
+
- Monetary values
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
text: The text to extract entities from
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Set of entity strings
|
|
101
|
+
"""
|
|
102
|
+
entities = set()
|
|
103
|
+
|
|
104
|
+
# Capitalized words/phrases (proper nouns)
|
|
105
|
+
# Match sequences of capitalized words
|
|
106
|
+
proper_nouns = re.findall(r"\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b", text)
|
|
107
|
+
# Filter out sentence starters by checking position
|
|
108
|
+
for noun in proper_nouns:
|
|
109
|
+
# Check if it appears mid-sentence (not after . or at start)
|
|
110
|
+
pattern = rf"[a-z,;:]\s+{re.escape(noun)}\b"
|
|
111
|
+
if re.search(pattern, text) or text.strip().startswith(noun):
|
|
112
|
+
entities.add(noun)
|
|
113
|
+
|
|
114
|
+
# Dates - various formats
|
|
115
|
+
date_patterns = [
|
|
116
|
+
r"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b", # 01/15/2024
|
|
117
|
+
r"\b(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}(?:st|nd|rd|th)?,?\s+\d{4}\b",
|
|
118
|
+
r"\b\d{1,2}(?:st|nd|rd|th)?\s+(?:January|February|March|April|May|June|July|August|September|October|November|December),?\s+\d{4}\b",
|
|
119
|
+
r"\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\.?\s+\d{1,2},?\s+\d{4}\b",
|
|
120
|
+
r"\b\d{4}\b", # Years
|
|
121
|
+
]
|
|
122
|
+
for pattern in date_patterns:
|
|
123
|
+
matches = re.findall(pattern, text, re.IGNORECASE)
|
|
124
|
+
entities.update(matches)
|
|
125
|
+
|
|
126
|
+
# Monetary values
|
|
127
|
+
money_patterns = [
|
|
128
|
+
r"\$[\d,]+(?:\.\d{2})?(?:\s*(?:million|billion|trillion))?",
|
|
129
|
+
r"[\d,]+(?:\.\d{2})?\s*(?:dollars?|USD|euros?|EUR|pounds?|GBP)",
|
|
130
|
+
r"\d+(?:\.\d+)?\s*(?:million|billion|trillion)\s*(?:dollars?|euros?|pounds?)?",
|
|
131
|
+
]
|
|
132
|
+
for pattern in money_patterns:
|
|
133
|
+
matches = re.findall(pattern, text, re.IGNORECASE)
|
|
134
|
+
entities.update(matches)
|
|
135
|
+
|
|
136
|
+
# Percentages
|
|
137
|
+
percentages = re.findall(r"\b\d+(?:\.\d+)?%", text)
|
|
138
|
+
entities.update(percentages)
|
|
139
|
+
|
|
140
|
+
# Quantities with units
|
|
141
|
+
quantities = re.findall(
|
|
142
|
+
r"\b\d+(?:\.\d+)?\s*(?:kg|km|miles?|meters?|feet|inches|pounds?|ounces?|liters?|gallons?|hours?|minutes?|seconds?|days?|weeks?|months?|years?)\b",
|
|
143
|
+
text,
|
|
144
|
+
re.IGNORECASE,
|
|
145
|
+
)
|
|
146
|
+
entities.update(quantities)
|
|
147
|
+
|
|
148
|
+
# Email addresses
|
|
149
|
+
emails = re.findall(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", text)
|
|
150
|
+
entities.update(emails)
|
|
151
|
+
|
|
152
|
+
# URLs
|
|
153
|
+
urls = re.findall(r"https?://[^\s]+", text)
|
|
154
|
+
entities.update(urls)
|
|
155
|
+
|
|
156
|
+
return entities
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def extract_entities(
|
|
160
|
+
text: str,
|
|
161
|
+
entity_types: Optional[List[str]] = None,
|
|
162
|
+
use_llm: bool = False,
|
|
163
|
+
) -> Set[str]:
|
|
164
|
+
"""
|
|
165
|
+
Main entity extraction function.
|
|
166
|
+
|
|
167
|
+
Tries spaCy first, falls back to heuristics.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
text: The text to extract entities from
|
|
171
|
+
entity_types: Optional list of entity types to filter by
|
|
172
|
+
use_llm: Whether to use LLM for extraction (not implemented)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Set of entity strings
|
|
176
|
+
"""
|
|
177
|
+
if use_llm:
|
|
178
|
+
# LLM-based extraction could be added here
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
if _SPACY_AVAILABLE:
|
|
182
|
+
return extract_entities_spacy(text, entity_types)
|
|
183
|
+
|
|
184
|
+
return extract_entities_heuristic(text)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def normalize_entity(entity: str) -> str:
|
|
188
|
+
"""
|
|
189
|
+
Normalize an entity for comparison.
|
|
190
|
+
|
|
191
|
+
Handles case, whitespace, and common variations.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
entity: The entity string to normalize
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Normalized entity string
|
|
198
|
+
"""
|
|
199
|
+
# Lowercase
|
|
200
|
+
normalized = entity.lower().strip()
|
|
201
|
+
|
|
202
|
+
# Normalize whitespace
|
|
203
|
+
normalized = re.sub(r"\s+", " ", normalized)
|
|
204
|
+
|
|
205
|
+
# Remove common prefixes/suffixes
|
|
206
|
+
normalized = re.sub(r"^(the|a|an)\s+", "", normalized)
|
|
207
|
+
|
|
208
|
+
return normalized
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def entities_match(entity1: str, entity2: str, threshold: float = 0.8) -> bool:
|
|
212
|
+
"""
|
|
213
|
+
Check if two entities match.
|
|
214
|
+
|
|
215
|
+
Handles exact matches and fuzzy matching for variations.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
entity1: First entity
|
|
219
|
+
entity2: Second entity
|
|
220
|
+
threshold: Similarity threshold for fuzzy matching
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
True if entities match
|
|
224
|
+
"""
|
|
225
|
+
norm1 = normalize_entity(entity1)
|
|
226
|
+
norm2 = normalize_entity(entity2)
|
|
227
|
+
|
|
228
|
+
# Exact match
|
|
229
|
+
if norm1 == norm2:
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
# Substring match
|
|
233
|
+
if norm1 in norm2 or norm2 in norm1:
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
# Simple character overlap for fuzzy matching
|
|
237
|
+
if len(norm1) > 3 and len(norm2) > 3:
|
|
238
|
+
chars1 = set(norm1)
|
|
239
|
+
chars2 = set(norm2)
|
|
240
|
+
overlap = len(chars1 & chars2) / max(len(chars1), len(chars2))
|
|
241
|
+
if overlap >= threshold:
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
return False
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Natural Language Inference utilities for RAG evaluation.
|
|
3
|
+
|
|
4
|
+
Provides entailment checking between claims and context.
|
|
5
|
+
Reuses the shared NLI pipeline from the hallucination module to avoid
|
|
6
|
+
loading the model twice. Falls back to word-overlap heuristic when
|
|
7
|
+
transformers is not installed.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Tuple, List, Optional
|
|
11
|
+
|
|
12
|
+
from ...hallucination.nli import (
|
|
13
|
+
NLILabel,
|
|
14
|
+
check_entailment,
|
|
15
|
+
check_entailment_heuristic,
|
|
16
|
+
check_contradiction,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Re-export for backwards compatibility
|
|
20
|
+
__all__ = [
|
|
21
|
+
"NLILabel",
|
|
22
|
+
"check_entailment",
|
|
23
|
+
"check_entailment_heuristic",
|
|
24
|
+
"check_claim_supported",
|
|
25
|
+
"check_attribution",
|
|
26
|
+
"check_contradiction",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def check_claim_supported(
|
|
31
|
+
claim: str, contexts: List[str], threshold: float = 0.5
|
|
32
|
+
) -> Tuple[bool, float, Optional[str]]:
|
|
33
|
+
"""
|
|
34
|
+
Check if a claim is supported by any of the contexts.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
claim: The claim to verify
|
|
38
|
+
contexts: List of context passages
|
|
39
|
+
threshold: Minimum score to consider claim supported
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Tuple of (is_supported, best_score, best_supporting_context)
|
|
43
|
+
"""
|
|
44
|
+
best_score = 0.0
|
|
45
|
+
best_context = None
|
|
46
|
+
|
|
47
|
+
for ctx in contexts:
|
|
48
|
+
label, score = check_entailment(ctx, claim)
|
|
49
|
+
|
|
50
|
+
if label == NLILabel.ENTAILMENT and score > best_score:
|
|
51
|
+
best_score = score
|
|
52
|
+
best_context = ctx[:200] + "..." if len(ctx) > 200 else ctx
|
|
53
|
+
|
|
54
|
+
is_supported = best_score >= threshold
|
|
55
|
+
|
|
56
|
+
return is_supported, best_score, best_context
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def check_attribution(
|
|
60
|
+
sentence: str, contexts: List[str], threshold: float = 0.5
|
|
61
|
+
) -> Tuple[bool, Optional[str], float]:
|
|
62
|
+
"""
|
|
63
|
+
Check if a sentence can be attributed to any context.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
sentence: The sentence to check attribution for
|
|
67
|
+
contexts: List of context passages
|
|
68
|
+
threshold: Minimum score to consider attributed
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Tuple of (is_attributed, best_context, best_score)
|
|
72
|
+
"""
|
|
73
|
+
best_score = 0.0
|
|
74
|
+
best_context = None
|
|
75
|
+
|
|
76
|
+
for ctx in contexts:
|
|
77
|
+
label, score = check_entailment(ctx, sentence)
|
|
78
|
+
|
|
79
|
+
if label == NLILabel.ENTAILMENT:
|
|
80
|
+
if score > best_score:
|
|
81
|
+
best_score = score
|
|
82
|
+
best_context = ctx[:200] + "..." if len(ctx) > 200 else ctx
|
|
83
|
+
elif label == NLILabel.NEUTRAL and score > 0.5:
|
|
84
|
+
# High-confidence neutral as partial attribution
|
|
85
|
+
adjusted_score = score * 0.7
|
|
86
|
+
if adjusted_score > best_score:
|
|
87
|
+
best_score = adjusted_score
|
|
88
|
+
best_context = ctx[:200] + "..." if len(ctx) > 200 else ctx
|
|
89
|
+
|
|
90
|
+
is_attributed = best_score >= threshold
|
|
91
|
+
|
|
92
|
+
return is_attributed, best_context, best_score
|