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,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Guardrails — content screening for AI applications.
|
|
3
|
+
|
|
4
|
+
Screen user input, LLM output, and RAG chunks for safety threats
|
|
5
|
+
using scanners (fast, local, no API keys) and model backends
|
|
6
|
+
(Turing, OpenAI, Azure, or local models via VLLM/HuggingFace).
|
|
7
|
+
|
|
8
|
+
Quick Start — Scanners Only (no API keys needed):
|
|
9
|
+
from fi.evals.guardrails.scanners import (
|
|
10
|
+
ScannerPipeline, JailbreakScanner, CodeInjectionScanner, SecretsScanner,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
pipeline = ScannerPipeline([
|
|
14
|
+
JailbreakScanner(),
|
|
15
|
+
CodeInjectionScanner(),
|
|
16
|
+
SecretsScanner(),
|
|
17
|
+
])
|
|
18
|
+
result = pipeline.scan("user input here")
|
|
19
|
+
if not result.passed:
|
|
20
|
+
print(f"Blocked: {result.blocked_by}")
|
|
21
|
+
|
|
22
|
+
Full Guardrails (with model backend):
|
|
23
|
+
from fi.evals.guardrails import Guardrails, GuardrailsConfig, GuardrailModel
|
|
24
|
+
|
|
25
|
+
guardrails = Guardrails(
|
|
26
|
+
config=GuardrailsConfig(models=[GuardrailModel.OPENAI_MODERATION])
|
|
27
|
+
)
|
|
28
|
+
result = guardrails.screen_input("user message")
|
|
29
|
+
|
|
30
|
+
Ensemble with Weighted Voting:
|
|
31
|
+
from fi.evals.guardrails import (
|
|
32
|
+
Guardrails, GuardrailsConfig, GuardrailModel, AggregationStrategy,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
config = GuardrailsConfig(
|
|
36
|
+
models=[GuardrailModel.TURING_FLASH, GuardrailModel.OPENAI_MODERATION],
|
|
37
|
+
aggregation=AggregationStrategy.WEIGHTED,
|
|
38
|
+
model_weights={"turing_flash": 2.0, "openai-moderation": 1.0},
|
|
39
|
+
)
|
|
40
|
+
guardrails = Guardrails(config=config)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# Config classes (always available — no external dependencies)
|
|
44
|
+
from fi.evals.guardrails.config import (
|
|
45
|
+
GuardrailModel,
|
|
46
|
+
RailType,
|
|
47
|
+
AggregationStrategy,
|
|
48
|
+
SafetyCategory,
|
|
49
|
+
GuardrailsConfig,
|
|
50
|
+
ScannerConfig,
|
|
51
|
+
TopicConfig,
|
|
52
|
+
LanguageConfig,
|
|
53
|
+
RegexPatternConfig,
|
|
54
|
+
)
|
|
55
|
+
from fi.evals.guardrails.types import (
|
|
56
|
+
GuardrailResult,
|
|
57
|
+
GuardrailsResponse,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Optional imports that depend on fi.api (backends, etc.)
|
|
61
|
+
_full_api_available = False
|
|
62
|
+
try:
|
|
63
|
+
from fi.evals.guardrails.base import Guardrails
|
|
64
|
+
from fi.evals.guardrails.gateway import (
|
|
65
|
+
GuardrailsGateway,
|
|
66
|
+
ScreeningSession,
|
|
67
|
+
AsyncScreeningSession,
|
|
68
|
+
)
|
|
69
|
+
_full_api_available = True
|
|
70
|
+
except (ImportError, ModuleNotFoundError):
|
|
71
|
+
Guardrails = None
|
|
72
|
+
GuardrailsGateway = None
|
|
73
|
+
ScreeningSession = None
|
|
74
|
+
AsyncScreeningSession = None
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
# Main class
|
|
78
|
+
"Guardrails",
|
|
79
|
+
# Configuration
|
|
80
|
+
"GuardrailsConfig",
|
|
81
|
+
"GuardrailModel",
|
|
82
|
+
"RailType",
|
|
83
|
+
"AggregationStrategy",
|
|
84
|
+
"SafetyCategory",
|
|
85
|
+
"ScannerConfig",
|
|
86
|
+
"TopicConfig",
|
|
87
|
+
"LanguageConfig",
|
|
88
|
+
"RegexPatternConfig",
|
|
89
|
+
# Response types
|
|
90
|
+
"GuardrailResult",
|
|
91
|
+
"GuardrailsResponse",
|
|
92
|
+
# Gateway
|
|
93
|
+
"GuardrailsGateway",
|
|
94
|
+
"ScreeningSession",
|
|
95
|
+
"AsyncScreeningSession",
|
|
96
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Guardrails Backends — model-based content screening.
|
|
3
|
+
|
|
4
|
+
Users normally don't import backends directly. Instead, configure via
|
|
5
|
+
GuardrailModel enum in GuardrailsConfig and let the registry handle
|
|
6
|
+
instantiation.
|
|
7
|
+
|
|
8
|
+
For custom backends, subclass BaseBackend or LocalModelBackend.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from fi.evals.guardrails.backends.base import BaseBackend
|
|
12
|
+
from fi.evals.guardrails.backends.turing import TuringBackend
|
|
13
|
+
|
|
14
|
+
# API backends
|
|
15
|
+
from fi.evals.guardrails.backends.openai import OpenAIBackend
|
|
16
|
+
from fi.evals.guardrails.backends.azure import AzureBackend
|
|
17
|
+
|
|
18
|
+
# Local model base (for custom backends)
|
|
19
|
+
from fi.evals.guardrails.backends.local_base import LocalModelBackend
|
|
20
|
+
|
|
21
|
+
# Local model backends
|
|
22
|
+
from fi.evals.guardrails.backends.wildguard import WildGuardBackend
|
|
23
|
+
from fi.evals.guardrails.backends.llamaguard import LlamaGuardBackend
|
|
24
|
+
from fi.evals.guardrails.backends.granite import GraniteGuardianBackend
|
|
25
|
+
from fi.evals.guardrails.backends.qwen import Qwen3GuardBackend
|
|
26
|
+
from fi.evals.guardrails.backends.shieldgemma import ShieldGemmaBackend
|
|
27
|
+
from fi.evals.guardrails.backends.generic_llm import GenericLLMGuardBackend
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
# Base classes (for extension)
|
|
31
|
+
"BaseBackend",
|
|
32
|
+
"LocalModelBackend",
|
|
33
|
+
# Backends
|
|
34
|
+
"TuringBackend",
|
|
35
|
+
"OpenAIBackend",
|
|
36
|
+
"AzureBackend",
|
|
37
|
+
"WildGuardBackend",
|
|
38
|
+
"LlamaGuardBackend",
|
|
39
|
+
"GraniteGuardianBackend",
|
|
40
|
+
"Qwen3GuardBackend",
|
|
41
|
+
"ShieldGemmaBackend",
|
|
42
|
+
"GenericLLMGuardBackend",
|
|
43
|
+
]
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Azure Content Safety Backend for Guardrails.
|
|
3
|
+
|
|
4
|
+
Uses Azure AI Content Safety API for content screening.
|
|
5
|
+
Supports 4 categories with severity levels 0-7.
|
|
6
|
+
|
|
7
|
+
Docs: https://learn.microsoft.com/en-us/azure/ai-services/content-safety/
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from fi.evals.guardrails.backends.base import BaseBackend
|
|
15
|
+
from fi.evals.guardrails.config import GuardrailModel, RailType
|
|
16
|
+
from fi.evals.guardrails.types import GuardrailResult
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Category mapping from Azure to our standard categories
|
|
20
|
+
AZURE_CATEGORY_MAP = {
|
|
21
|
+
"Hate": "hate_speech",
|
|
22
|
+
"SelfHarm": "self_harm",
|
|
23
|
+
"Sexual": "sexual_content",
|
|
24
|
+
"Violence": "violence",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Severity level to score mapping (0-7 to 0-1)
|
|
28
|
+
SEVERITY_TO_SCORE = {
|
|
29
|
+
0: 0.0,
|
|
30
|
+
1: 0.14,
|
|
31
|
+
2: 0.29,
|
|
32
|
+
3: 0.43,
|
|
33
|
+
4: 0.57,
|
|
34
|
+
5: 0.71,
|
|
35
|
+
6: 0.86,
|
|
36
|
+
7: 1.0,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AzureBackend(BaseBackend):
|
|
41
|
+
"""
|
|
42
|
+
Azure Content Safety API backend.
|
|
43
|
+
|
|
44
|
+
Uses Azure AI Content Safety for content moderation.
|
|
45
|
+
Supports 4 categories: Hate, SelfHarm, Sexual, Violence.
|
|
46
|
+
Each category has severity levels from 0 (safe) to 7 (severe).
|
|
47
|
+
|
|
48
|
+
Usage:
|
|
49
|
+
backend = AzureBackend(
|
|
50
|
+
model=GuardrailModel.AZURE_CONTENT_SAFETY,
|
|
51
|
+
endpoint="https://your-resource.cognitiveservices.azure.com/",
|
|
52
|
+
api_key="your-key" # or set AZURE_CONTENT_SAFETY_KEY env var
|
|
53
|
+
)
|
|
54
|
+
results = backend.classify("some content", RailType.INPUT)
|
|
55
|
+
|
|
56
|
+
Environment Variables:
|
|
57
|
+
AZURE_CONTENT_SAFETY_ENDPOINT: Azure endpoint URL
|
|
58
|
+
AZURE_CONTENT_SAFETY_KEY: Azure API key
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
model: GuardrailModel,
|
|
64
|
+
endpoint: Optional[str] = None,
|
|
65
|
+
api_key: Optional[str] = None,
|
|
66
|
+
severity_threshold: int = 2,
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Initialize Azure Content Safety backend.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
model: The guardrail model enum value
|
|
73
|
+
endpoint: Azure Content Safety endpoint URL
|
|
74
|
+
api_key: Azure API key
|
|
75
|
+
severity_threshold: Minimum severity level to flag (0-7, default: 2)
|
|
76
|
+
"""
|
|
77
|
+
super().__init__(model)
|
|
78
|
+
|
|
79
|
+
self._endpoint = endpoint or os.environ.get("AZURE_CONTENT_SAFETY_ENDPOINT")
|
|
80
|
+
self._api_key = api_key or os.environ.get("AZURE_CONTENT_SAFETY_KEY")
|
|
81
|
+
|
|
82
|
+
if not self._endpoint:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Azure endpoint required. Set AZURE_CONTENT_SAFETY_ENDPOINT environment variable "
|
|
85
|
+
"or pass endpoint parameter."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not self._api_key:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"Azure API key required. Set AZURE_CONTENT_SAFETY_KEY environment variable "
|
|
91
|
+
"or pass api_key parameter."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
self._severity_threshold = severity_threshold
|
|
95
|
+
self._client = None
|
|
96
|
+
|
|
97
|
+
def _get_client(self):
|
|
98
|
+
"""Lazy-load the Azure Content Safety client."""
|
|
99
|
+
if self._client is None:
|
|
100
|
+
try:
|
|
101
|
+
from azure.ai.contentsafety import ContentSafetyClient
|
|
102
|
+
from azure.core.credentials import AzureKeyCredential
|
|
103
|
+
except ImportError:
|
|
104
|
+
raise ImportError(
|
|
105
|
+
"azure-ai-contentsafety package required for Azure backend. "
|
|
106
|
+
"Install with: pip install azure-ai-contentsafety"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
self._client = ContentSafetyClient(
|
|
110
|
+
endpoint=self._endpoint,
|
|
111
|
+
credential=AzureKeyCredential(self._api_key),
|
|
112
|
+
)
|
|
113
|
+
return self._client
|
|
114
|
+
|
|
115
|
+
def classify(
|
|
116
|
+
self,
|
|
117
|
+
content: str,
|
|
118
|
+
rail_type: RailType,
|
|
119
|
+
context: Optional[str] = None,
|
|
120
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
121
|
+
) -> List[GuardrailResult]:
|
|
122
|
+
"""
|
|
123
|
+
Classify content using Azure Content Safety API.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
content: Content to classify
|
|
127
|
+
rail_type: Type of rail (input, output, retrieval)
|
|
128
|
+
context: Optional context (not used by Azure)
|
|
129
|
+
metadata: Optional metadata (not used by Azure)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
List of GuardrailResult objects for flagged categories
|
|
133
|
+
"""
|
|
134
|
+
start_time = time.time()
|
|
135
|
+
results = []
|
|
136
|
+
|
|
137
|
+
# Handle empty content
|
|
138
|
+
if not content or not content.strip():
|
|
139
|
+
return [
|
|
140
|
+
GuardrailResult(
|
|
141
|
+
passed=True,
|
|
142
|
+
category="empty",
|
|
143
|
+
score=0.0,
|
|
144
|
+
model=self.model_name,
|
|
145
|
+
reason="Empty or whitespace-only content",
|
|
146
|
+
action="pass",
|
|
147
|
+
latency_ms=(time.time() - start_time) * 1000,
|
|
148
|
+
)
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
from azure.ai.contentsafety.models import AnalyzeTextOptions, TextCategory
|
|
153
|
+
|
|
154
|
+
client = self._get_client()
|
|
155
|
+
|
|
156
|
+
# Truncate content if too long (Azure limit is 10,000 characters)
|
|
157
|
+
truncated_content = content[:10000] if len(content) > 10000 else content
|
|
158
|
+
|
|
159
|
+
# Create the analysis request
|
|
160
|
+
request = AnalyzeTextOptions(
|
|
161
|
+
text=truncated_content,
|
|
162
|
+
categories=[
|
|
163
|
+
TextCategory.HATE,
|
|
164
|
+
TextCategory.SELF_HARM,
|
|
165
|
+
TextCategory.SEXUAL,
|
|
166
|
+
TextCategory.VIOLENCE,
|
|
167
|
+
],
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Call Azure Content Safety API
|
|
171
|
+
response = client.analyze_text(request)
|
|
172
|
+
|
|
173
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
174
|
+
|
|
175
|
+
# Check if any category exceeds threshold
|
|
176
|
+
flagged = False
|
|
177
|
+
for category_result in response.categories_analysis:
|
|
178
|
+
category_name = category_result.category
|
|
179
|
+
severity = category_result.severity
|
|
180
|
+
|
|
181
|
+
if severity >= self._severity_threshold:
|
|
182
|
+
flagged = True
|
|
183
|
+
our_category = AZURE_CATEGORY_MAP.get(
|
|
184
|
+
category_name, category_name.lower()
|
|
185
|
+
)
|
|
186
|
+
score = SEVERITY_TO_SCORE.get(severity, severity / 7.0)
|
|
187
|
+
|
|
188
|
+
results.append(
|
|
189
|
+
GuardrailResult(
|
|
190
|
+
passed=False,
|
|
191
|
+
category=our_category,
|
|
192
|
+
score=score,
|
|
193
|
+
model=self.model_name,
|
|
194
|
+
reason=f"Content flagged for {category_name} (severity: {severity})",
|
|
195
|
+
action="block",
|
|
196
|
+
latency_ms=elapsed_ms,
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if not flagged:
|
|
201
|
+
return [
|
|
202
|
+
GuardrailResult(
|
|
203
|
+
passed=True,
|
|
204
|
+
category="safe",
|
|
205
|
+
score=0.0,
|
|
206
|
+
model=self.model_name,
|
|
207
|
+
reason="Content passed all Azure safety checks",
|
|
208
|
+
action="pass",
|
|
209
|
+
latency_ms=elapsed_ms,
|
|
210
|
+
)
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
except ImportError as e:
|
|
214
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
215
|
+
results.append(
|
|
216
|
+
GuardrailResult(
|
|
217
|
+
passed=False,
|
|
218
|
+
category="error",
|
|
219
|
+
score=0.0,
|
|
220
|
+
model=self.model_name,
|
|
221
|
+
reason=str(e),
|
|
222
|
+
action="block",
|
|
223
|
+
latency_ms=elapsed_ms,
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
229
|
+
results.append(
|
|
230
|
+
GuardrailResult(
|
|
231
|
+
passed=False,
|
|
232
|
+
category="error",
|
|
233
|
+
score=0.0,
|
|
234
|
+
model=self.model_name,
|
|
235
|
+
reason=f"Azure API error: {str(e)}",
|
|
236
|
+
action="block",
|
|
237
|
+
latency_ms=elapsed_ms,
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return results
|
|
242
|
+
|
|
243
|
+
async def classify_async(
|
|
244
|
+
self,
|
|
245
|
+
content: str,
|
|
246
|
+
rail_type: RailType,
|
|
247
|
+
context: Optional[str] = None,
|
|
248
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
249
|
+
) -> List[GuardrailResult]:
|
|
250
|
+
"""
|
|
251
|
+
Async version using Azure's async client.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
content: Content to classify
|
|
255
|
+
rail_type: Type of rail
|
|
256
|
+
context: Optional context
|
|
257
|
+
metadata: Optional metadata
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
List of GuardrailResult objects
|
|
261
|
+
"""
|
|
262
|
+
start_time = time.time()
|
|
263
|
+
|
|
264
|
+
# Handle empty content
|
|
265
|
+
if not content or not content.strip():
|
|
266
|
+
return [
|
|
267
|
+
GuardrailResult(
|
|
268
|
+
passed=True,
|
|
269
|
+
category="empty",
|
|
270
|
+
score=0.0,
|
|
271
|
+
model=self.model_name,
|
|
272
|
+
reason="Empty or whitespace-only content",
|
|
273
|
+
action="pass",
|
|
274
|
+
latency_ms=(time.time() - start_time) * 1000,
|
|
275
|
+
)
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
from azure.ai.contentsafety.aio import ContentSafetyClient as AsyncContentSafetyClient
|
|
280
|
+
from azure.ai.contentsafety.models import AnalyzeTextOptions, TextCategory
|
|
281
|
+
from azure.core.credentials import AzureKeyCredential
|
|
282
|
+
except ImportError:
|
|
283
|
+
# Fall back to sync implementation
|
|
284
|
+
return await super().classify_async(content, rail_type, context, metadata)
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
async with AsyncContentSafetyClient(
|
|
288
|
+
endpoint=self._endpoint,
|
|
289
|
+
credential=AzureKeyCredential(self._api_key),
|
|
290
|
+
) as async_client:
|
|
291
|
+
|
|
292
|
+
truncated_content = content[:10000] if len(content) > 10000 else content
|
|
293
|
+
|
|
294
|
+
request = AnalyzeTextOptions(
|
|
295
|
+
text=truncated_content,
|
|
296
|
+
categories=[
|
|
297
|
+
TextCategory.HATE,
|
|
298
|
+
TextCategory.SELF_HARM,
|
|
299
|
+
TextCategory.SEXUAL,
|
|
300
|
+
TextCategory.VIOLENCE,
|
|
301
|
+
],
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
response = await async_client.analyze_text(request)
|
|
305
|
+
|
|
306
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
307
|
+
|
|
308
|
+
results = []
|
|
309
|
+
flagged = False
|
|
310
|
+
|
|
311
|
+
for category_result in response.categories_analysis:
|
|
312
|
+
category_name = category_result.category
|
|
313
|
+
severity = category_result.severity
|
|
314
|
+
|
|
315
|
+
if severity >= self._severity_threshold:
|
|
316
|
+
flagged = True
|
|
317
|
+
our_category = AZURE_CATEGORY_MAP.get(
|
|
318
|
+
category_name, category_name.lower()
|
|
319
|
+
)
|
|
320
|
+
score = SEVERITY_TO_SCORE.get(severity, severity / 7.0)
|
|
321
|
+
|
|
322
|
+
results.append(
|
|
323
|
+
GuardrailResult(
|
|
324
|
+
passed=False,
|
|
325
|
+
category=our_category,
|
|
326
|
+
score=score,
|
|
327
|
+
model=self.model_name,
|
|
328
|
+
reason=f"Content flagged for {category_name} (severity: {severity})",
|
|
329
|
+
action="block",
|
|
330
|
+
latency_ms=elapsed_ms,
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
if not flagged:
|
|
335
|
+
return [
|
|
336
|
+
GuardrailResult(
|
|
337
|
+
passed=True,
|
|
338
|
+
category="safe",
|
|
339
|
+
score=0.0,
|
|
340
|
+
model=self.model_name,
|
|
341
|
+
reason="Content passed all Azure safety checks",
|
|
342
|
+
action="pass",
|
|
343
|
+
latency_ms=elapsed_ms,
|
|
344
|
+
)
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
return results
|
|
348
|
+
|
|
349
|
+
except Exception as e:
|
|
350
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
351
|
+
return [
|
|
352
|
+
GuardrailResult(
|
|
353
|
+
passed=False,
|
|
354
|
+
category="error",
|
|
355
|
+
score=0.0,
|
|
356
|
+
model=self.model_name,
|
|
357
|
+
reason=f"Azure API error: {str(e)}",
|
|
358
|
+
action="block",
|
|
359
|
+
latency_ms=elapsed_ms,
|
|
360
|
+
)
|
|
361
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Backend Interface for Guardrails.
|
|
3
|
+
|
|
4
|
+
Defines the abstract interface that all guardrail backends must implement.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Dict, List, Optional, Any
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
from fi.evals.guardrails.config import RailType, GuardrailModel
|
|
12
|
+
from fi.evals.guardrails.types import GuardrailResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseBackend(ABC):
|
|
16
|
+
"""
|
|
17
|
+
Abstract base class for guardrail backends.
|
|
18
|
+
|
|
19
|
+
All backend implementations must inherit from this class and implement
|
|
20
|
+
the classify and classify_async methods.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, model: GuardrailModel):
|
|
24
|
+
"""
|
|
25
|
+
Initialize the backend.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
model: The model this backend represents
|
|
29
|
+
"""
|
|
30
|
+
self.model = model
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def classify(
|
|
34
|
+
self,
|
|
35
|
+
content: str,
|
|
36
|
+
rail_type: RailType,
|
|
37
|
+
context: Optional[str] = None,
|
|
38
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
39
|
+
) -> List[GuardrailResult]:
|
|
40
|
+
"""
|
|
41
|
+
Classify content synchronously.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
content: The content to classify
|
|
45
|
+
rail_type: Type of rail (input, output, retrieval)
|
|
46
|
+
context: Optional context for the classification
|
|
47
|
+
metadata: Optional metadata
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of GuardrailResult objects for each category detected
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
async def classify_async(
|
|
55
|
+
self,
|
|
56
|
+
content: str,
|
|
57
|
+
rail_type: RailType,
|
|
58
|
+
context: Optional[str] = None,
|
|
59
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
60
|
+
) -> List[GuardrailResult]:
|
|
61
|
+
"""
|
|
62
|
+
Classify content asynchronously.
|
|
63
|
+
|
|
64
|
+
Default implementation wraps the sync method.
|
|
65
|
+
Backends can override for true async support.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
content: The content to classify
|
|
69
|
+
rail_type: Type of rail (input, output, retrieval)
|
|
70
|
+
context: Optional context for the classification
|
|
71
|
+
metadata: Optional metadata
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List of GuardrailResult objects for each category detected
|
|
75
|
+
"""
|
|
76
|
+
loop = asyncio.get_event_loop()
|
|
77
|
+
return await loop.run_in_executor(
|
|
78
|
+
None,
|
|
79
|
+
lambda: self.classify(content, rail_type, context, metadata)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def model_name(self) -> str:
|
|
84
|
+
"""Get the model name."""
|
|
85
|
+
return self.model.value
|
|
86
|
+
|
|
87
|
+
def __repr__(self) -> str:
|
|
88
|
+
return f"{self.__class__.__name__}(model={self.model.value})"
|