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,609 @@
|
|
|
1
|
+
"""Custom evaluation builder for creating evaluations without full classes.
|
|
2
|
+
|
|
3
|
+
This module provides a builder pattern and decorator-based approach
|
|
4
|
+
for defining custom evaluations with minimal boilerplate.
|
|
5
|
+
|
|
6
|
+
Example using decorator:
|
|
7
|
+
from fi.evals.framework.evals.builder import custom_eval
|
|
8
|
+
|
|
9
|
+
@custom_eval("sentiment_score")
|
|
10
|
+
def evaluate_sentiment(inputs):
|
|
11
|
+
text = inputs["text"]
|
|
12
|
+
# Your evaluation logic here
|
|
13
|
+
positive_words = ["good", "great", "excellent"]
|
|
14
|
+
score = sum(1 for w in text.lower().split() if w in positive_words) / 10
|
|
15
|
+
return {"score": min(1.0, score), "passed": score > 0.5}
|
|
16
|
+
|
|
17
|
+
Example using builder:
|
|
18
|
+
from fi.evals.framework.evals.builder import EvalBuilder
|
|
19
|
+
|
|
20
|
+
sentiment_eval = (
|
|
21
|
+
EvalBuilder("sentiment_score")
|
|
22
|
+
.version("1.0.0")
|
|
23
|
+
.required_fields(["text"])
|
|
24
|
+
.threshold(0.5)
|
|
25
|
+
.evaluator(lambda inputs: {"score": 0.8, "passed": True})
|
|
26
|
+
.build()
|
|
27
|
+
)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
32
|
+
|
|
33
|
+
from ..protocols import BaseEvaluation, register_evaluation
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CustomEvalResult:
|
|
38
|
+
"""Result from a custom evaluation.
|
|
39
|
+
|
|
40
|
+
This flexible result type supports any evaluation output format.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
score: Evaluation score between 0 and 1
|
|
44
|
+
passed: Whether the evaluation passed
|
|
45
|
+
confidence: Confidence in the result (0-1)
|
|
46
|
+
details: Additional evaluation details
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
score: float
|
|
50
|
+
passed: bool
|
|
51
|
+
confidence: float = 1.0
|
|
52
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_dict(cls, data: Dict[str, Any]) -> "CustomEvalResult":
|
|
56
|
+
"""Create result from dictionary."""
|
|
57
|
+
return cls(
|
|
58
|
+
score=data.get("score", 0.0),
|
|
59
|
+
passed=data.get("passed", False),
|
|
60
|
+
confidence=data.get("confidence", 1.0),
|
|
61
|
+
details={k: v for k, v in data.items()
|
|
62
|
+
if k not in ("score", "passed", "confidence")},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CustomEvaluation(BaseEvaluation):
|
|
67
|
+
"""Dynamically created evaluation from builder or decorator."""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
name: str,
|
|
72
|
+
evaluator_fn: Callable[[Dict[str, Any]], Union[Dict[str, Any], CustomEvalResult]],
|
|
73
|
+
version: str = "1.0.0",
|
|
74
|
+
required_fields: Optional[List[str]] = None,
|
|
75
|
+
threshold: float = 0.7,
|
|
76
|
+
description: str = "",
|
|
77
|
+
span_attributes_fn: Optional[Callable[[Any], Dict[str, Any]]] = None,
|
|
78
|
+
):
|
|
79
|
+
"""Initialize custom evaluation.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
name: Unique evaluation name
|
|
83
|
+
evaluator_fn: Function that performs the evaluation
|
|
84
|
+
version: Evaluation version
|
|
85
|
+
required_fields: List of required input fields
|
|
86
|
+
threshold: Score threshold for passing
|
|
87
|
+
description: Human-readable description
|
|
88
|
+
span_attributes_fn: Optional function to extract span attributes
|
|
89
|
+
"""
|
|
90
|
+
self._name = name
|
|
91
|
+
self._version = version
|
|
92
|
+
self._evaluator_fn = evaluator_fn
|
|
93
|
+
self._required_fields = required_fields or []
|
|
94
|
+
self._threshold = threshold
|
|
95
|
+
self._description = description
|
|
96
|
+
self._span_attributes_fn = span_attributes_fn
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def name(self) -> str:
|
|
100
|
+
return self._name
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def version(self) -> str:
|
|
104
|
+
return self._version
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def threshold(self) -> float:
|
|
108
|
+
return self._threshold
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def description(self) -> str:
|
|
112
|
+
return self._description
|
|
113
|
+
|
|
114
|
+
def validate_inputs(self, inputs: Dict[str, Any]) -> List[str]:
|
|
115
|
+
"""Validate required inputs."""
|
|
116
|
+
errors = []
|
|
117
|
+
for field_name in self._required_fields:
|
|
118
|
+
if field_name not in inputs:
|
|
119
|
+
errors.append(f"Missing required field: {field_name}")
|
|
120
|
+
return errors
|
|
121
|
+
|
|
122
|
+
def evaluate(self, inputs: Dict[str, Any]) -> CustomEvalResult:
|
|
123
|
+
"""Run the evaluation.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
inputs: Dictionary of evaluation inputs
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
CustomEvalResult with evaluation outcome
|
|
130
|
+
"""
|
|
131
|
+
result = self._evaluator_fn(inputs)
|
|
132
|
+
|
|
133
|
+
if isinstance(result, CustomEvalResult):
|
|
134
|
+
return result
|
|
135
|
+
elif isinstance(result, dict):
|
|
136
|
+
return CustomEvalResult.from_dict(result)
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Evaluator function must return dict or CustomEvalResult, "
|
|
140
|
+
f"got {type(result).__name__}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def get_span_attributes(self, result: CustomEvalResult) -> Dict[str, Any]:
|
|
144
|
+
"""Get span attributes for tracing.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
result: Evaluation result
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Dictionary of span attributes
|
|
151
|
+
"""
|
|
152
|
+
if self._span_attributes_fn:
|
|
153
|
+
return self._span_attributes_fn(result)
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
"score": result.score,
|
|
157
|
+
"passed": result.passed,
|
|
158
|
+
"confidence": result.confidence,
|
|
159
|
+
"threshold": self._threshold,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class EvalBuilder:
|
|
164
|
+
"""Builder for creating custom evaluations.
|
|
165
|
+
|
|
166
|
+
Provides a fluent interface for configuring evaluations.
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
eval = (
|
|
170
|
+
EvalBuilder("my_eval")
|
|
171
|
+
.version("2.0.0")
|
|
172
|
+
.required_fields(["text", "reference"])
|
|
173
|
+
.threshold(0.8)
|
|
174
|
+
.description("Checks text quality")
|
|
175
|
+
.evaluator(my_evaluation_function)
|
|
176
|
+
.build()
|
|
177
|
+
)
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def __init__(self, name: str):
|
|
181
|
+
"""Initialize builder with evaluation name.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
name: Unique evaluation name
|
|
185
|
+
"""
|
|
186
|
+
self._name = name
|
|
187
|
+
self._version = "1.0.0"
|
|
188
|
+
self._required_fields: List[str] = []
|
|
189
|
+
self._threshold = 0.7
|
|
190
|
+
self._description = ""
|
|
191
|
+
self._evaluator_fn: Optional[Callable] = None
|
|
192
|
+
self._span_attributes_fn: Optional[Callable] = None
|
|
193
|
+
self._register = False
|
|
194
|
+
|
|
195
|
+
def version(self, version: str) -> "EvalBuilder":
|
|
196
|
+
"""Set evaluation version.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
version: Semantic version string
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Self for chaining
|
|
203
|
+
"""
|
|
204
|
+
self._version = version
|
|
205
|
+
return self
|
|
206
|
+
|
|
207
|
+
def required_fields(self, fields: List[str]) -> "EvalBuilder":
|
|
208
|
+
"""Set required input fields.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
fields: List of field names
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Self for chaining
|
|
215
|
+
"""
|
|
216
|
+
self._required_fields = fields
|
|
217
|
+
return self
|
|
218
|
+
|
|
219
|
+
def require(self, *fields: str) -> "EvalBuilder":
|
|
220
|
+
"""Add required input fields.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
*fields: Field names to require
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Self for chaining
|
|
227
|
+
"""
|
|
228
|
+
self._required_fields.extend(fields)
|
|
229
|
+
return self
|
|
230
|
+
|
|
231
|
+
def threshold(self, threshold: float) -> "EvalBuilder":
|
|
232
|
+
"""Set score threshold for passing.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
threshold: Threshold value (0-1)
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Self for chaining
|
|
239
|
+
"""
|
|
240
|
+
self._threshold = threshold
|
|
241
|
+
return self
|
|
242
|
+
|
|
243
|
+
def description(self, description: str) -> "EvalBuilder":
|
|
244
|
+
"""Set human-readable description.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
description: Description text
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Self for chaining
|
|
251
|
+
"""
|
|
252
|
+
self._description = description
|
|
253
|
+
return self
|
|
254
|
+
|
|
255
|
+
def evaluator(
|
|
256
|
+
self,
|
|
257
|
+
fn: Callable[[Dict[str, Any]], Union[Dict[str, Any], CustomEvalResult]]
|
|
258
|
+
) -> "EvalBuilder":
|
|
259
|
+
"""Set the evaluator function.
|
|
260
|
+
|
|
261
|
+
The function should take an inputs dict and return either:
|
|
262
|
+
- A dict with at least "score" and "passed" keys
|
|
263
|
+
- A CustomEvalResult object
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
fn: Evaluator function
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Self for chaining
|
|
270
|
+
"""
|
|
271
|
+
self._evaluator_fn = fn
|
|
272
|
+
return self
|
|
273
|
+
|
|
274
|
+
def span_attributes(
|
|
275
|
+
self,
|
|
276
|
+
fn: Callable[[CustomEvalResult], Dict[str, Any]]
|
|
277
|
+
) -> "EvalBuilder":
|
|
278
|
+
"""Set custom span attributes extractor.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
fn: Function to extract span attributes from result
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Self for chaining
|
|
285
|
+
"""
|
|
286
|
+
self._span_attributes_fn = fn
|
|
287
|
+
return self
|
|
288
|
+
|
|
289
|
+
def auto_register(self, register: bool = True) -> "EvalBuilder":
|
|
290
|
+
"""Enable automatic registration with EvalRegistry.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
register: Whether to auto-register
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Self for chaining
|
|
297
|
+
"""
|
|
298
|
+
self._register = register
|
|
299
|
+
return self
|
|
300
|
+
|
|
301
|
+
def build(self) -> CustomEvaluation:
|
|
302
|
+
"""Build the custom evaluation.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Configured CustomEvaluation instance
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
ValueError: If evaluator function not set
|
|
309
|
+
"""
|
|
310
|
+
if self._evaluator_fn is None:
|
|
311
|
+
raise ValueError("Evaluator function must be set via .evaluator()")
|
|
312
|
+
|
|
313
|
+
evaluation = CustomEvaluation(
|
|
314
|
+
name=self._name,
|
|
315
|
+
evaluator_fn=self._evaluator_fn,
|
|
316
|
+
version=self._version,
|
|
317
|
+
required_fields=self._required_fields,
|
|
318
|
+
threshold=self._threshold,
|
|
319
|
+
description=self._description,
|
|
320
|
+
span_attributes_fn=self._span_attributes_fn,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if self._register:
|
|
324
|
+
register_evaluation(evaluation.__class__)
|
|
325
|
+
|
|
326
|
+
return evaluation
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def custom_eval(
|
|
330
|
+
name: str,
|
|
331
|
+
version: str = "1.0.0",
|
|
332
|
+
required_fields: Optional[List[str]] = None,
|
|
333
|
+
threshold: float = 0.7,
|
|
334
|
+
description: str = "",
|
|
335
|
+
auto_register: bool = False,
|
|
336
|
+
) -> Callable:
|
|
337
|
+
"""Decorator for creating custom evaluations from functions.
|
|
338
|
+
|
|
339
|
+
The decorated function should take an inputs dict and return either:
|
|
340
|
+
- A dict with at least "score" and "passed" keys
|
|
341
|
+
- A CustomEvalResult object
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
name: Unique evaluation name
|
|
345
|
+
version: Evaluation version
|
|
346
|
+
required_fields: List of required input fields
|
|
347
|
+
threshold: Score threshold for passing
|
|
348
|
+
description: Human-readable description
|
|
349
|
+
auto_register: Whether to register with EvalRegistry
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Decorator function
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
@custom_eval("sentiment", required_fields=["text"])
|
|
356
|
+
def evaluate_sentiment(inputs):
|
|
357
|
+
text = inputs["text"]
|
|
358
|
+
score = analyze_sentiment(text)
|
|
359
|
+
return {"score": score, "passed": score > 0.5}
|
|
360
|
+
|
|
361
|
+
# Use it
|
|
362
|
+
result = evaluate_sentiment.evaluate({"text": "Great product!"})
|
|
363
|
+
"""
|
|
364
|
+
def decorator(fn: Callable) -> CustomEvaluation:
|
|
365
|
+
evaluation = CustomEvaluation(
|
|
366
|
+
name=name,
|
|
367
|
+
evaluator_fn=fn,
|
|
368
|
+
version=version,
|
|
369
|
+
required_fields=required_fields or [],
|
|
370
|
+
threshold=threshold,
|
|
371
|
+
description=description or fn.__doc__ or "",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if auto_register:
|
|
375
|
+
# Create a class to register
|
|
376
|
+
eval_class = type(
|
|
377
|
+
f"Custom_{name}",
|
|
378
|
+
(CustomEvaluation,),
|
|
379
|
+
{"name": name, "version": version}
|
|
380
|
+
)
|
|
381
|
+
register_evaluation(eval_class)
|
|
382
|
+
|
|
383
|
+
return evaluation
|
|
384
|
+
|
|
385
|
+
return decorator
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def simple_eval(
|
|
389
|
+
name: str,
|
|
390
|
+
scorer: Callable[[Dict[str, Any]], float],
|
|
391
|
+
threshold: float = 0.7,
|
|
392
|
+
required_fields: Optional[List[str]] = None,
|
|
393
|
+
) -> CustomEvaluation:
|
|
394
|
+
"""Create a simple evaluation from a scoring function.
|
|
395
|
+
|
|
396
|
+
This is the simplest way to create a custom evaluation when you
|
|
397
|
+
only need to compute a score.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
name: Unique evaluation name
|
|
401
|
+
scorer: Function that takes inputs and returns a score (0-1)
|
|
402
|
+
threshold: Score threshold for passing
|
|
403
|
+
required_fields: List of required input fields
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
CustomEvaluation instance
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
word_count_eval = simple_eval(
|
|
410
|
+
"word_count",
|
|
411
|
+
scorer=lambda inputs: min(1.0, len(inputs["text"].split()) / 100),
|
|
412
|
+
threshold=0.5,
|
|
413
|
+
required_fields=["text"],
|
|
414
|
+
)
|
|
415
|
+
"""
|
|
416
|
+
def evaluator(inputs: Dict[str, Any]) -> Dict[str, Any]:
|
|
417
|
+
score = scorer(inputs)
|
|
418
|
+
return {
|
|
419
|
+
"score": score,
|
|
420
|
+
"passed": score >= threshold,
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return CustomEvaluation(
|
|
424
|
+
name=name,
|
|
425
|
+
evaluator_fn=evaluator,
|
|
426
|
+
required_fields=required_fields or [],
|
|
427
|
+
threshold=threshold,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def comparison_eval(
|
|
432
|
+
name: str,
|
|
433
|
+
comparator: Callable[[Any, Any], float],
|
|
434
|
+
source_field: str = "response",
|
|
435
|
+
target_field: str = "reference",
|
|
436
|
+
threshold: float = 0.7,
|
|
437
|
+
) -> CustomEvaluation:
|
|
438
|
+
"""Create an evaluation that compares two fields.
|
|
439
|
+
|
|
440
|
+
This is useful for evaluations that compare a response to a reference.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
name: Unique evaluation name
|
|
444
|
+
comparator: Function that compares source and target, returns score (0-1)
|
|
445
|
+
source_field: Name of the source field
|
|
446
|
+
target_field: Name of the target field
|
|
447
|
+
threshold: Score threshold for passing
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
CustomEvaluation instance
|
|
451
|
+
|
|
452
|
+
Example:
|
|
453
|
+
length_match_eval = comparison_eval(
|
|
454
|
+
"length_match",
|
|
455
|
+
comparator=lambda src, tgt: 1 - abs(len(src) - len(tgt)) / max(len(src), len(tgt), 1),
|
|
456
|
+
threshold=0.8,
|
|
457
|
+
)
|
|
458
|
+
"""
|
|
459
|
+
def evaluator(inputs: Dict[str, Any]) -> Dict[str, Any]:
|
|
460
|
+
source = inputs.get(source_field, "")
|
|
461
|
+
target = inputs.get(target_field, "")
|
|
462
|
+
score = comparator(source, target)
|
|
463
|
+
return {
|
|
464
|
+
"score": score,
|
|
465
|
+
"passed": score >= threshold,
|
|
466
|
+
"source_field": source_field,
|
|
467
|
+
"target_field": target_field,
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return CustomEvaluation(
|
|
471
|
+
name=name,
|
|
472
|
+
evaluator_fn=evaluator,
|
|
473
|
+
required_fields=[source_field, target_field],
|
|
474
|
+
threshold=threshold,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def threshold_eval(
|
|
479
|
+
name: str,
|
|
480
|
+
metric_fn: Callable[[Dict[str, Any]], float],
|
|
481
|
+
min_threshold: Optional[float] = None,
|
|
482
|
+
max_threshold: Optional[float] = None,
|
|
483
|
+
required_fields: Optional[List[str]] = None,
|
|
484
|
+
) -> CustomEvaluation:
|
|
485
|
+
"""Create an evaluation based on min/max thresholds.
|
|
486
|
+
|
|
487
|
+
Pass if the metric is within the specified range.
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
name: Unique evaluation name
|
|
491
|
+
metric_fn: Function that computes the metric value
|
|
492
|
+
min_threshold: Minimum acceptable value (inclusive)
|
|
493
|
+
max_threshold: Maximum acceptable value (inclusive)
|
|
494
|
+
required_fields: List of required input fields
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
CustomEvaluation instance
|
|
498
|
+
|
|
499
|
+
Example:
|
|
500
|
+
length_eval = threshold_eval(
|
|
501
|
+
"response_length",
|
|
502
|
+
metric_fn=lambda inputs: len(inputs["response"]),
|
|
503
|
+
min_threshold=10,
|
|
504
|
+
max_threshold=500,
|
|
505
|
+
required_fields=["response"],
|
|
506
|
+
)
|
|
507
|
+
"""
|
|
508
|
+
def evaluator(inputs: Dict[str, Any]) -> Dict[str, Any]:
|
|
509
|
+
value = metric_fn(inputs)
|
|
510
|
+
|
|
511
|
+
passed = True
|
|
512
|
+
if min_threshold is not None and value < min_threshold:
|
|
513
|
+
passed = False
|
|
514
|
+
if max_threshold is not None and value > max_threshold:
|
|
515
|
+
passed = False
|
|
516
|
+
|
|
517
|
+
# Normalize score to 0-1
|
|
518
|
+
if min_threshold is not None and max_threshold is not None:
|
|
519
|
+
range_val = max_threshold - min_threshold
|
|
520
|
+
if range_val > 0:
|
|
521
|
+
score = max(0, min(1, (value - min_threshold) / range_val))
|
|
522
|
+
else:
|
|
523
|
+
score = 1.0 if value == min_threshold else 0.0
|
|
524
|
+
elif min_threshold is not None:
|
|
525
|
+
score = 1.0 if value >= min_threshold else value / min_threshold
|
|
526
|
+
elif max_threshold is not None:
|
|
527
|
+
score = 1.0 if value <= max_threshold else max_threshold / value
|
|
528
|
+
else:
|
|
529
|
+
score = 1.0
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
"score": score,
|
|
533
|
+
"passed": passed,
|
|
534
|
+
"value": value,
|
|
535
|
+
"min_threshold": min_threshold,
|
|
536
|
+
"max_threshold": max_threshold,
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return CustomEvaluation(
|
|
540
|
+
name=name,
|
|
541
|
+
evaluator_fn=evaluator,
|
|
542
|
+
required_fields=required_fields or [],
|
|
543
|
+
threshold=0.5, # Not used, but required
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def pattern_match_eval(
|
|
548
|
+
name: str,
|
|
549
|
+
patterns: List[str],
|
|
550
|
+
field: str = "response",
|
|
551
|
+
mode: str = "any", # "any", "all", "none"
|
|
552
|
+
case_sensitive: bool = False,
|
|
553
|
+
) -> CustomEvaluation:
|
|
554
|
+
"""Create an evaluation based on pattern matching.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
name: Unique evaluation name
|
|
558
|
+
patterns: List of regex patterns
|
|
559
|
+
field: Field to check patterns against
|
|
560
|
+
mode: "any" (pass if any match), "all" (pass if all match),
|
|
561
|
+
"none" (pass if none match)
|
|
562
|
+
case_sensitive: Whether patterns are case-sensitive
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
CustomEvaluation instance
|
|
566
|
+
|
|
567
|
+
Example:
|
|
568
|
+
has_greeting = pattern_match_eval(
|
|
569
|
+
"has_greeting",
|
|
570
|
+
patterns=[r"\\b(hello|hi|hey)\\b"],
|
|
571
|
+
field="response",
|
|
572
|
+
mode="any",
|
|
573
|
+
)
|
|
574
|
+
"""
|
|
575
|
+
import re
|
|
576
|
+
flags = 0 if case_sensitive else re.IGNORECASE
|
|
577
|
+
|
|
578
|
+
compiled_patterns = [re.compile(p, flags) for p in patterns]
|
|
579
|
+
|
|
580
|
+
def evaluator(inputs: Dict[str, Any]) -> Dict[str, Any]:
|
|
581
|
+
text = str(inputs.get(field, ""))
|
|
582
|
+
matches = [bool(p.search(text)) for p in compiled_patterns]
|
|
583
|
+
|
|
584
|
+
if mode == "any":
|
|
585
|
+
passed = any(matches)
|
|
586
|
+
score = sum(matches) / len(matches) if matches else 0
|
|
587
|
+
elif mode == "all":
|
|
588
|
+
passed = all(matches)
|
|
589
|
+
score = sum(matches) / len(matches) if matches else 0
|
|
590
|
+
elif mode == "none":
|
|
591
|
+
passed = not any(matches)
|
|
592
|
+
score = 1 - (sum(matches) / len(matches)) if matches else 1
|
|
593
|
+
else:
|
|
594
|
+
raise ValueError(f"Unknown mode: {mode}")
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
"score": score,
|
|
598
|
+
"passed": passed,
|
|
599
|
+
"mode": mode,
|
|
600
|
+
"matches": matches,
|
|
601
|
+
"pattern_count": len(patterns),
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return CustomEvaluation(
|
|
605
|
+
name=name,
|
|
606
|
+
evaluator_fn=evaluator,
|
|
607
|
+
required_fields=[field],
|
|
608
|
+
threshold=0.5,
|
|
609
|
+
)
|