agent-learning-kit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_learning_kit-0.1.0.dist-info/METADATA +381 -0
- agent_learning_kit-0.1.0.dist-info/RECORD +642 -0
- agent_learning_kit-0.1.0.dist-info/WHEEL +4 -0
- agent_learning_kit-0.1.0.dist-info/entry_points.txt +5 -0
- agent_learning_kit-0.1.0.dist-info/licenses/LICENSE +173 -0
- agent_learning_kit-0.1.0.dist-info/licenses/NOTICE +7 -0
- fi/__init__.py +5 -0
- fi/alk/__init__.py +57 -0
- fi/alk/_facade.py +31 -0
- fi/alk/_module_alias.py +68 -0
- fi/alk/_paths.py +14 -0
- fi/alk/_schema.py +522 -0
- fi/alk/actions.py +727 -0
- fi/alk/bench/__init__.py +517 -0
- fi/alk/bench/_codeexec.py +213 -0
- fi/alk/bench/_coding.py +215 -0
- fi/alk/bench/_docker.py +237 -0
- fi/alk/bench/_grader.py +286 -0
- fi/alk/bench/_pull.py +212 -0
- fi/alk/bench/_voice.py +147 -0
- fi/alk/capabilities.py +627 -0
- fi/alk/cli.py +6396 -0
- fi/alk/config.py +130 -0
- fi/alk/cua_loop.py +562 -0
- fi/alk/evals.py +2351 -0
- fi/alk/extensions.py +163 -0
- fi/alk/harness/ARCHITECTURE.md +231 -0
- fi/alk/harness/DESIGN.md +246 -0
- fi/alk/harness/ENVIRONMENT_CONFORMANCE.md +127 -0
- fi/alk/harness/HOW-IT-WORKS.md +297 -0
- fi/alk/harness/IMPLEMENTATION_AND_VALIDATION_STATUS.md +229 -0
- fi/alk/harness/README.md +417 -0
- fi/alk/harness/__init__.py +77 -0
- fi/alk/harness/__main__.py +3 -0
- fi/alk/harness/amend.py +312 -0
- fi/alk/harness/artifacts.py +319 -0
- fi/alk/harness/authoring_entrypoint.py +189 -0
- fi/alk/harness/authoring_runtime_validation.py +267 -0
- fi/alk/harness/backends/README.md +43 -0
- fi/alk/harness/backends/__init__.py +122 -0
- fi/alk/harness/backends/base.py +241 -0
- fi/alk/harness/backends/claude.py +211 -0
- fi/alk/harness/backends/files.py +182 -0
- fi/alk/harness/backends/vertex_gemini.py +457 -0
- fi/alk/harness/background_noise.py +95 -0
- fi/alk/harness/build.py +385 -0
- fi/alk/harness/bundle.py +593 -0
- fi/alk/harness/bundle_author_v2.py +1831 -0
- fi/alk/harness/bundle_v2.py +719 -0
- fi/alk/harness/call_runner.py +1440 -0
- fi/alk/harness/callback_http_adapter.py +111 -0
- fi/alk/harness/catalogue.py +287 -0
- fi/alk/harness/chat.py +428 -0
- fi/alk/harness/chat_call_runner.py +506 -0
- fi/alk/harness/checks.py +136 -0
- fi/alk/harness/cli.py +1354 -0
- fi/alk/harness/config.py +338 -0
- fi/alk/harness/contract.py +718 -0
- fi/alk/harness/credentials.py +674 -0
- fi/alk/harness/data/persona_vocabulary.json +111 -0
- fi/alk/harness/environment.py +99 -0
- fi/alk/harness/environment_plan.py +168 -0
- fi/alk/harness/events.py +125 -0
- fi/alk/harness/executor.py +304 -0
- fi/alk/harness/folder.py +234 -0
- fi/alk/harness/generated_runtime.py +815 -0
- fi/alk/harness/github.py +72 -0
- fi/alk/harness/hosted_authoring_entrypoint.py +183 -0
- fi/alk/harness/hosted_entrypoint.py +2402 -0
- fi/alk/harness/hosted_scheduler.py +2218 -0
- fi/alk/harness/job.py +426 -0
- fi/alk/harness/judge.py +184 -0
- fi/alk/harness/livekit_source.py +50 -0
- fi/alk/harness/livekit_tool_trace_bootstrap.py +71 -0
- fi/alk/harness/observability.py +208 -0
- fi/alk/harness/outbound.py +3252 -0
- fi/alk/harness/packaging.py +515 -0
- fi/alk/harness/persona_guides.py +157 -0
- fi/alk/harness/platform.py +692 -0
- fi/alk/harness/process_preflight.py +764 -0
- fi/alk/harness/process_runtime.py +5670 -0
- fi/alk/harness/prove.py +425 -0
- fi/alk/harness/provider_import.py +703 -0
- fi/alk/harness/provider_lifecycle.py +392 -0
- fi/alk/harness/provision.py +2896 -0
- fi/alk/harness/reception.py +147 -0
- fi/alk/harness/retell_chat_call_runner.py +373 -0
- fi/alk/harness/run/__init__.py +296 -0
- fi/alk/harness/run/alk.py +184 -0
- fi/alk/harness/run/call.py +162 -0
- fi/alk/harness/run/conversation.py +264 -0
- fi/alk/harness/run/data/voices_by_language_and_gender.json +693 -0
- fi/alk/harness/run/evidence.py +195 -0
- fi/alk/harness/run/grade.py +598 -0
- fi/alk/harness/run/live.py +297 -0
- fi/alk/harness/run/models.py +56 -0
- fi/alk/harness/run/platform_evals.py +227 -0
- fi/alk/harness/run/sdk_voice.py +130 -0
- fi/alk/harness/run/simulation.py +1209 -0
- fi/alk/harness/run/stage.py +91 -0
- fi/alk/harness/run/targets.py +508 -0
- fi/alk/harness/run/tools.py +601 -0
- fi/alk/harness/run/voice.py +340 -0
- fi/alk/harness/runtime.py +172 -0
- fi/alk/harness/sandbox_server.py +2011 -0
- fi/alk/harness/sandbox_worker.py +44 -0
- fi/alk/harness/scenario.py +1048 -0
- fi/alk/harness/scenario_source.py +879 -0
- fi/alk/harness/scenario_tools.py +1143 -0
- fi/alk/harness/scenarios.py +915 -0
- fi/alk/harness/secrets.py +168 -0
- fi/alk/harness/service_catalog.py +97 -0
- fi/alk/harness/session.py +391 -0
- fi/alk/harness/sessions.py +372 -0
- fi/alk/harness/simulator.py +76 -0
- fi/alk/harness/simulator_voice.py +928 -0
- fi/alk/harness/skills/build-environment/SKILL.md +538 -0
- fi/alk/harness/skills/harness.md +131 -0
- fi/alk/harness/skills/kinds/chat.md +48 -0
- fi/alk/harness/skills/kinds/voice-voicemail.md +63 -0
- fi/alk/harness/skills/kinds/voice.md +59 -0
- fi/alk/harness/skills/plan-suite/SKILL.md +103 -0
- fi/alk/harness/skills/provision-environment/SKILL.md +136 -0
- fi/alk/harness/skills/run-scenarios/SKILL.md +112 -0
- fi/alk/harness/skills/understand-agent/SKILL.md +251 -0
- fi/alk/harness/skills/write-scenarios/SKILL.md +606 -0
- fi/alk/harness/skills/write-scenarios/references/refusals.md +28 -0
- fi/alk/harness/skills/write-scenarios/references/world-api.md +92 -0
- fi/alk/harness/source_data_invariants.py +444 -0
- fi/alk/harness/source_tool_evidence.py +79 -0
- fi/alk/harness/sources.py +253 -0
- fi/alk/harness/spend.py +140 -0
- fi/alk/harness/tool_trace_proxy.py +104 -0
- fi/alk/harness/tools.py +1018 -0
- fi/alk/harness/understand.py +169 -0
- fi/alk/harness/voicemail_audio.py +74 -0
- fi/alk/harness/world/__init__.py +33 -0
- fi/alk/harness/world/errors.py +68 -0
- fi/alk/harness/world/expectations.py +91 -0
- fi/alk/harness/world/handle.py +538 -0
- fi/alk/harness/world/kinds.py +196 -0
- fi/alk/harness/world/mutate.py +186 -0
- fi/alk/harness/world/probe.py +413 -0
- fi/alk/harness/world/provision.py +511 -0
- fi/alk/harness/world/provisioned.py +191 -0
- fi/alk/harness/world/runtime.py +616 -0
- fi/alk/harness/world/snapshot.py +288 -0
- fi/alk/harness/world/stores/__init__.py +305 -0
- fi/alk/harness/world/stores/container.py +215 -0
- fi/alk/harness/world/stores/inprocess.py +346 -0
- fi/alk/harness/world/stores/postgres.py +481 -0
- fi/alk/harness/world/stores/prove.py +202 -0
- fi/alk/harness/world/stores/sqlite.py +245 -0
- fi/alk/harness/world/stores/written.py +182 -0
- fi/alk/harness/world/tools.py +1516 -0
- fi/alk/harness/world/workspace.py +144 -0
- fi/alk/image_loop.py +453 -0
- fi/alk/image_perturb.py +241 -0
- fi/alk/improve.py +274 -0
- fi/alk/live/__init__.py +154 -0
- fi/alk/live/_attribution.py +184 -0
- fi/alk/live/_capture.py +264 -0
- fi/alk/live/_codec.py +391 -0
- fi/alk/live/_contract.py +134 -0
- fi/alk/live/_loopback.py +316 -0
- fi/alk/live/_perturb.py +449 -0
- fi/alk/live/_runner.py +386 -0
- fi/alk/live/_stats.py +561 -0
- fi/alk/live/_transcript.py +240 -0
- fi/alk/live/_workers/__init__.py +9 -0
- fi/alk/live/_workers/a2a_worker.py +316 -0
- fi/alk/live/_workers/langgraph_worker.py +217 -0
- fi/alk/live/_workers/livekit_worker.py +207 -0
- fi/alk/live/_workers/mcp_loopback_server.py +46 -0
- fi/alk/live/_workers/mcp_worker.py +158 -0
- fi/alk/live/_workers/pipecat_worker.py +189 -0
- fi/alk/live/a2a_lane.py +138 -0
- fi/alk/live/langgraph_lane.py +339 -0
- fi/alk/live/livekit_lane.py +376 -0
- fi/alk/live/mcp_lane.py +172 -0
- fi/alk/live/pipecat_lane.py +341 -0
- fi/alk/live/voice_redteam.py +494 -0
- fi/alk/loss.py +306 -0
- fi/alk/optimize.py +36260 -0
- fi/alk/practice/__init__.py +51 -0
- fi/alk/practice/_assess.py +103 -0
- fi/alk/practice/_budget.py +81 -0
- fi/alk/practice/_calibrate.py +69 -0
- fi/alk/practice/_capstone.py +86 -0
- fi/alk/practice/_contract.py +91 -0
- fi/alk/practice/_diagnose.py +79 -0
- fi/alk/practice/_drill.py +196 -0
- fi/alk/practice/_experiment.py +720 -0
- fi/alk/practice/_schedule.py +102 -0
- fi/alk/practice/_store.py +194 -0
- fi/alk/practice/_trainer.py +245 -0
- fi/alk/practice/_update.py +125 -0
- fi/alk/redteam.py +2621 -0
- fi/alk/rewardhack.py +237 -0
- fi/alk/simulate.py +10351 -0
- fi/alk/studio/__init__.py +82 -0
- fi/alk/studio/_bias.py +314 -0
- fi/alk/studio/_calibration.py +522 -0
- fi/alk/studio/_coverage.py +262 -0
- fi/alk/studio/_download.py +665 -0
- fi/alk/studio/_fidelity_attack.py +114 -0
- fi/alk/studio/_generate.py +652 -0
- fi/alk/studio/_library.py +370 -0
- fi/alk/studio/_scan.py +134 -0
- fi/alk/studio/_upgrade.py +42 -0
- fi/alk/studio/_vendor.py +172 -0
- fi/alk/suite.py +4200 -0
- fi/alk/tasks.py +828 -0
- fi/alk/telemetry/__init__.py +149 -0
- fi/alk/telemetry/_contract.py +141 -0
- fi/alk/telemetry/_emit.py +182 -0
- fi/alk/telemetry/_ledger.py +296 -0
- fi/alk/telemetry/_queue.py +127 -0
- fi/alk/telemetry/_row.py +294 -0
- fi/alk/telemetry/_run.py +233 -0
- fi/alk/telemetry/_sync.py +193 -0
- fi/alk/telemetry/_url.py +119 -0
- fi/alk/trinity.py +49397 -0
- fi/alk/voice_loop.py +174 -0
- fi/api/__init__.py +1 -0
- fi/api/auth.py +137 -0
- fi/api/types.py +29 -0
- fi/cli/__init__.py +9 -0
- fi/cli/assertions/__init__.py +25 -0
- fi/cli/assertions/conditions.py +76 -0
- fi/cli/assertions/evaluator.py +286 -0
- fi/cli/assertions/exit_codes.py +20 -0
- fi/cli/assertions/parser.py +131 -0
- fi/cli/assertions/reporter.py +194 -0
- fi/cli/commands/__init__.py +9 -0
- fi/cli/commands/config.py +165 -0
- fi/cli/commands/export.py +208 -0
- fi/cli/commands/init.py +112 -0
- fi/cli/commands/list_cmd.py +213 -0
- fi/cli/commands/run.py +486 -0
- fi/cli/commands/validate.py +173 -0
- fi/cli/commands/view.py +424 -0
- fi/cli/config/__init__.py +6 -0
- fi/cli/config/defaults.py +206 -0
- fi/cli/config/loader.py +155 -0
- fi/cli/config/schema.py +174 -0
- fi/cli/main.py +78 -0
- fi/cli/output/__init__.py +6 -0
- fi/cli/output/formatters.py +106 -0
- fi/cli/output/reporters.py +46 -0
- fi/cli/storage/__init__.py +5 -0
- fi/cli/storage/run_history.py +249 -0
- fi/cli/utils/__init__.py +5 -0
- fi/cli/utils/console.py +44 -0
- fi/evals/__init__.py +131 -0
- fi/evals/autoeval/__init__.py +137 -0
- fi/evals/autoeval/analyzer.py +211 -0
- fi/evals/autoeval/config.py +244 -0
- fi/evals/autoeval/export.py +213 -0
- fi/evals/autoeval/interactive.py +283 -0
- fi/evals/autoeval/pipeline.py +625 -0
- fi/evals/autoeval/prompts.py +139 -0
- fi/evals/autoeval/recommender.py +242 -0
- fi/evals/autoeval/rules.py +589 -0
- fi/evals/autoeval/templates.py +299 -0
- fi/evals/autoeval/types.py +232 -0
- fi/evals/core/__init__.py +16 -0
- fi/evals/core/cloud_registry.py +184 -0
- fi/evals/core/engines.py +368 -0
- fi/evals/core/evaluate.py +319 -0
- fi/evals/core/judge_prompt.py +90 -0
- fi/evals/core/prompt_generator.py +83 -0
- fi/evals/core/registry.py +57 -0
- fi/evals/core/result.py +55 -0
- fi/evals/evaluator.py +721 -0
- fi/evals/execution.py +168 -0
- fi/evals/feedback/__init__.py +32 -0
- fi/evals/feedback/calibrator.py +160 -0
- fi/evals/feedback/collector.py +214 -0
- fi/evals/feedback/hooks.py +81 -0
- fi/evals/feedback/retriever.py +128 -0
- fi/evals/feedback/store.py +272 -0
- fi/evals/feedback/types.py +99 -0
- fi/evals/framework/README.md +79 -0
- fi/evals/framework/__init__.py +267 -0
- fi/evals/framework/backends/Dockerfile.eval-runner +33 -0
- fi/evals/framework/backends/__init__.py +99 -0
- fi/evals/framework/backends/_container.py +141 -0
- fi/evals/framework/backends/_utils.py +145 -0
- fi/evals/framework/backends/base.py +223 -0
- fi/evals/framework/backends/celery_backend.py +417 -0
- fi/evals/framework/backends/celery_worker.py +78 -0
- fi/evals/framework/backends/kubernetes_backend.py +665 -0
- fi/evals/framework/backends/ray_backend.py +521 -0
- fi/evals/framework/backends/temporal.py +350 -0
- fi/evals/framework/backends/temporal_worker.py +126 -0
- fi/evals/framework/backends/thread_pool.py +286 -0
- fi/evals/framework/context.py +258 -0
- fi/evals/framework/enrichment.py +306 -0
- fi/evals/framework/evals/__init__.py +68 -0
- fi/evals/framework/evals/agentic.py +399 -0
- fi/evals/framework/evals/builder.py +609 -0
- fi/evals/framework/evals/semantic.py +142 -0
- fi/evals/framework/evaluator.py +647 -0
- fi/evals/framework/evaluators/__init__.py +22 -0
- fi/evals/framework/evaluators/blocking.py +347 -0
- fi/evals/framework/evaluators/non_blocking.py +577 -0
- fi/evals/framework/propagation.py +421 -0
- fi/evals/framework/protocols.py +385 -0
- fi/evals/framework/registry.py +370 -0
- fi/evals/framework/resilience/__init__.py +150 -0
- fi/evals/framework/resilience/circuit_breaker.py +309 -0
- fi/evals/framework/resilience/degradation.py +355 -0
- fi/evals/framework/resilience/health.py +505 -0
- fi/evals/framework/resilience/rate_limiter.py +228 -0
- fi/evals/framework/resilience/retry.py +274 -0
- fi/evals/framework/resilience/types.py +288 -0
- fi/evals/framework/resilience/wrapper.py +433 -0
- fi/evals/framework/types.py +218 -0
- fi/evals/guardrails/README.md +915 -0
- fi/evals/guardrails/__init__.py +96 -0
- fi/evals/guardrails/backends/__init__.py +43 -0
- fi/evals/guardrails/backends/azure.py +361 -0
- fi/evals/guardrails/backends/base.py +88 -0
- fi/evals/guardrails/backends/generic_llm.py +163 -0
- fi/evals/guardrails/backends/granite.py +216 -0
- fi/evals/guardrails/backends/llamaguard.py +221 -0
- fi/evals/guardrails/backends/local_base.py +479 -0
- fi/evals/guardrails/backends/openai.py +365 -0
- fi/evals/guardrails/backends/qwen.py +170 -0
- fi/evals/guardrails/backends/shieldgemma.py +154 -0
- fi/evals/guardrails/backends/turing.py +235 -0
- fi/evals/guardrails/backends/vllm_client.py +321 -0
- fi/evals/guardrails/backends/wildguard.py +188 -0
- fi/evals/guardrails/base.py +888 -0
- fi/evals/guardrails/config.py +221 -0
- fi/evals/guardrails/discovery.py +243 -0
- fi/evals/guardrails/gateway.py +437 -0
- fi/evals/guardrails/registry.py +231 -0
- fi/evals/guardrails/scanners/__init__.py +127 -0
- fi/evals/guardrails/scanners/base.py +191 -0
- fi/evals/guardrails/scanners/code_injection.py +243 -0
- fi/evals/guardrails/scanners/eval_delegate.py +574 -0
- fi/evals/guardrails/scanners/invisible_chars.py +351 -0
- fi/evals/guardrails/scanners/jailbreak.py +412 -0
- fi/evals/guardrails/scanners/language.py +288 -0
- fi/evals/guardrails/scanners/pipeline.py +260 -0
- fi/evals/guardrails/scanners/regex.py +311 -0
- fi/evals/guardrails/scanners/secrets.py +274 -0
- fi/evals/guardrails/scanners/topics.py +649 -0
- fi/evals/guardrails/scanners/urls.py +341 -0
- fi/evals/guardrails/types.py +96 -0
- fi/evals/llm/__init__.py +3 -0
- fi/evals/llm/base_llm_provider.py +35 -0
- fi/evals/llm/providers/litellm.py +70 -0
- fi/evals/local/__init__.py +90 -0
- fi/evals/local/evaluator.py +690 -0
- fi/evals/local/execution_mode.py +121 -0
- fi/evals/local/llm.py +489 -0
- fi/evals/local/metrics/__init__.py +19 -0
- fi/evals/local/registry.py +360 -0
- fi/evals/manager.py +1018 -0
- fi/evals/manager_types.py +362 -0
- fi/evals/metrics/__init__.py +185 -0
- fi/evals/metrics/agents/__init__.py +74 -0
- fi/evals/metrics/agents/metrics.py +693 -0
- fi/evals/metrics/agents/report.py +36463 -0
- fi/evals/metrics/agents/types.py +160 -0
- fi/evals/metrics/base_llm_metric.py +111 -0
- fi/evals/metrics/base_metric.py +138 -0
- fi/evals/metrics/code_security/__init__.py +305 -0
- fi/evals/metrics/code_security/analyzer.py +985 -0
- fi/evals/metrics/code_security/benchmarks/__init__.py +73 -0
- fi/evals/metrics/code_security/benchmarks/builtin.py +750 -0
- fi/evals/metrics/code_security/benchmarks/loader.py +580 -0
- fi/evals/metrics/code_security/benchmarks/types.py +308 -0
- fi/evals/metrics/code_security/detectors/__init__.py +186 -0
- fi/evals/metrics/code_security/detectors/base.py +394 -0
- fi/evals/metrics/code_security/detectors/cryptography.py +345 -0
- fi/evals/metrics/code_security/detectors/injection.py +744 -0
- fi/evals/metrics/code_security/detectors/secrets.py +287 -0
- fi/evals/metrics/code_security/detectors/serialization.py +192 -0
- fi/evals/metrics/code_security/joint_metrics.py +588 -0
- fi/evals/metrics/code_security/judges/__init__.py +83 -0
- fi/evals/metrics/code_security/judges/base.py +238 -0
- fi/evals/metrics/code_security/judges/dual_judge.py +534 -0
- fi/evals/metrics/code_security/judges/llm_judge.py +301 -0
- fi/evals/metrics/code_security/judges/pattern_judge.py +515 -0
- fi/evals/metrics/code_security/metrics.py +388 -0
- fi/evals/metrics/code_security/modes/__init__.py +63 -0
- fi/evals/metrics/code_security/modes/adversarial.py +284 -0
- fi/evals/metrics/code_security/modes/autocomplete.py +198 -0
- fi/evals/metrics/code_security/modes/base.py +283 -0
- fi/evals/metrics/code_security/modes/instruct.py +253 -0
- fi/evals/metrics/code_security/modes/repair.py +230 -0
- fi/evals/metrics/code_security/reports/__init__.py +57 -0
- fi/evals/metrics/code_security/reports/generator.py +404 -0
- fi/evals/metrics/code_security/reports/leaderboard.py +509 -0
- fi/evals/metrics/code_security/types.py +534 -0
- fi/evals/metrics/function_calling/__init__.py +34 -0
- fi/evals/metrics/function_calling/metrics.py +573 -0
- fi/evals/metrics/function_calling/types.py +87 -0
- fi/evals/metrics/hallucination/__init__.py +54 -0
- fi/evals/metrics/hallucination/detector.py +149 -0
- fi/evals/metrics/hallucination/metrics.py +390 -0
- fi/evals/metrics/hallucination/nli.py +253 -0
- fi/evals/metrics/hallucination/sentinel.py +106 -0
- fi/evals/metrics/hallucination/types.py +132 -0
- fi/evals/metrics/heuristics/aggregation_metrics.py +85 -0
- fi/evals/metrics/heuristics/json_metrics.py +87 -0
- fi/evals/metrics/heuristics/similarity_metrics.py +375 -0
- fi/evals/metrics/heuristics/string_metrics.py +391 -0
- fi/evals/metrics/llm_as_judges/__init__.py +17 -0
- fi/evals/metrics/llm_as_judges/custom_judge/metric.py +112 -0
- fi/evals/metrics/llm_as_judges/custom_judge/prompts.py +26 -0
- fi/evals/metrics/llm_as_judges/types.py +48 -0
- fi/evals/metrics/rag/__init__.py +111 -0
- fi/evals/metrics/rag/advanced/__init__.py +14 -0
- fi/evals/metrics/rag/advanced/multi_hop.py +283 -0
- fi/evals/metrics/rag/advanced/source_attribution.py +344 -0
- fi/evals/metrics/rag/generation/__init__.py +17 -0
- fi/evals/metrics/rag/generation/answer_relevancy.py +176 -0
- fi/evals/metrics/rag/generation/context_utilization.py +245 -0
- fi/evals/metrics/rag/generation/faithfulness.py +241 -0
- fi/evals/metrics/rag/generation/groundedness.py +131 -0
- fi/evals/metrics/rag/rag_score.py +277 -0
- fi/evals/metrics/rag/retrieval/__init__.py +20 -0
- fi/evals/metrics/rag/retrieval/context_entity_recall.py +124 -0
- fi/evals/metrics/rag/retrieval/context_precision.py +158 -0
- fi/evals/metrics/rag/retrieval/context_recall.py +106 -0
- fi/evals/metrics/rag/retrieval/noise_sensitivity.py +163 -0
- fi/evals/metrics/rag/retrieval/ranking.py +261 -0
- fi/evals/metrics/rag/types.py +100 -0
- fi/evals/metrics/rag/utils/__init__.py +62 -0
- fi/evals/metrics/rag/utils/claims.py +189 -0
- fi/evals/metrics/rag/utils/entities.py +244 -0
- fi/evals/metrics/rag/utils/nli.py +92 -0
- fi/evals/metrics/rag/utils/similarity.py +345 -0
- fi/evals/metrics/structured/__init__.py +114 -0
- fi/evals/metrics/structured/field_completeness.py +313 -0
- fi/evals/metrics/structured/hierarchy_score.py +366 -0
- fi/evals/metrics/structured/json_validation.py +190 -0
- fi/evals/metrics/structured/schema_compliance.py +280 -0
- fi/evals/metrics/structured/structured_output_score.py +298 -0
- fi/evals/metrics/structured/types.py +108 -0
- fi/evals/metrics/structured/validators/__init__.py +30 -0
- fi/evals/metrics/structured/validators/base.py +189 -0
- fi/evals/metrics/structured/validators/json_validator.py +196 -0
- fi/evals/metrics/structured/validators/pydantic_validator.py +178 -0
- fi/evals/metrics/structured/validators/yaml_validator.py +248 -0
- fi/evals/otel/__init__.py +266 -0
- fi/evals/otel/config.py +400 -0
- fi/evals/otel/conventions.py +463 -0
- fi/evals/otel/enrichment.py +371 -0
- fi/evals/otel/instrumentors/__init__.py +140 -0
- fi/evals/otel/instrumentors/anthropic.py +517 -0
- fi/evals/otel/instrumentors/base.py +382 -0
- fi/evals/otel/instrumentors/openai.py +673 -0
- fi/evals/otel/processors/__init__.py +36 -0
- fi/evals/otel/processors/base.py +473 -0
- fi/evals/otel/processors/cost.py +445 -0
- fi/evals/otel/processors/evaluation.py +559 -0
- fi/evals/otel/processors/llm.py +462 -0
- fi/evals/otel/tracer.py +506 -0
- fi/evals/otel/types.py +232 -0
- fi/evals/otel_utils.py +23 -0
- fi/evals/protect.py +671 -0
- fi/evals/protect_input_adapter.py +154 -0
- fi/evals/streaming/__init__.py +88 -0
- fi/evals/streaming/buffer.py +213 -0
- fi/evals/streaming/evaluator.py +551 -0
- fi/evals/streaming/policy.py +307 -0
- fi/evals/streaming/scorers.py +368 -0
- fi/evals/streaming/types.py +238 -0
- fi/evals/templates.py +472 -0
- fi/evals/types.py +156 -0
- fi/opt/__init__.py +221 -0
- fi/opt/_objective_scoring.py +85 -0
- fi/opt/base/__init__.py +11 -0
- fi/opt/base/base_generator.py +33 -0
- fi/opt/base/base_mapper.py +26 -0
- fi/opt/base/base_optimizer.py +45 -0
- fi/opt/base/evaluator.py +211 -0
- fi/opt/components.py +3095 -0
- fi/opt/datamappers/__init__.py +3 -0
- fi/opt/datamappers/basic_mapper.py +40 -0
- fi/opt/deployment.py +1021 -0
- fi/opt/evidence.py +4332 -0
- fi/opt/generators/__init__.py +3 -0
- fi/opt/generators/litellm.py +66 -0
- fi/opt/integrations/__init__.py +23 -0
- fi/opt/integrations/generative_suite.py +410 -0
- fi/opt/integrations/simulate.py +1313 -0
- fi/opt/mutations.py +771 -0
- fi/opt/observability.py +4639 -0
- fi/opt/optimizer_trace.py +889 -0
- fi/opt/optimizers/__init__.py +80 -0
- fi/opt/optimizers/agent.py +331 -0
- fi/opt/optimizers/agent_bandit.py +392 -0
- fi/opt/optimizers/agent_curriculum.py +635 -0
- fi/opt/optimizers/agent_evolution.py +894 -0
- fi/opt/optimizers/agent_feedback.py +1863 -0
- fi/opt/optimizers/agent_pareto.py +547 -0
- fi/opt/optimizers/agent_social_memory.py +1113 -0
- fi/opt/optimizers/agent_tpe.py +321 -0
- fi/opt/optimizers/bayesian_search.py +449 -0
- fi/opt/optimizers/council.py +2075 -0
- fi/opt/optimizers/futureagi_replay.py +799 -0
- fi/opt/optimizers/gepa.py +322 -0
- fi/opt/optimizers/metaprompt.py +243 -0
- fi/opt/optimizers/promptwizard.py +417 -0
- fi/opt/optimizers/protegi.py +329 -0
- fi/opt/optimizers/random_search.py +224 -0
- fi/opt/research.py +518 -0
- fi/opt/simulation.py +260 -0
- fi/opt/targets.py +232 -0
- fi/opt/types.py +66 -0
- fi/opt/utils/__init__.py +4 -0
- fi/opt/utils/early_stopping.py +266 -0
- fi/opt/utils/setup_logging.py +82 -0
- fi/simulate/__init__.py +540 -0
- fi/simulate/_hashing.py +35 -0
- fi/simulate/_logging.py +10 -0
- fi/simulate/adapters.py +87 -0
- fi/simulate/agent/__init__.py +120 -0
- fi/simulate/agent/browser.py +658 -0
- fi/simulate/agent/definition.py +587 -0
- fi/simulate/agent/frameworks.py +3528 -0
- fi/simulate/agent/generic.py +8286 -0
- fi/simulate/agent/import_probe.py +227 -0
- fi/simulate/agent/memory.py +905 -0
- fi/simulate/agent/mocks.py +101 -0
- fi/simulate/agent/multi_agent.py +361 -0
- fi/simulate/agent/orchestration.py +903 -0
- fi/simulate/agent/realtime.py +665 -0
- fi/simulate/agent/wrapper.py +99 -0
- fi/simulate/agent/wrappers/__init__.py +18 -0
- fi/simulate/agent/wrappers/anthropic.py +62 -0
- fi/simulate/agent/wrappers/gemini.py +65 -0
- fi/simulate/agent/wrappers/http.py +404 -0
- fi/simulate/agent/wrappers/langchain.py +80 -0
- fi/simulate/agent/wrappers/openai.py +75 -0
- fi/simulate/agent/wrappers/websocket.py +326 -0
- fi/simulate/artifacts/__init__.py +11 -0
- fi/simulate/artifacts/manifest.py +62 -0
- fi/simulate/cli.py +20560 -0
- fi/simulate/endpoints/__init__.py +45 -0
- fi/simulate/endpoints/_http_actor.py +73 -0
- fi/simulate/endpoints/actor_sources.py +243 -0
- fi/simulate/endpoints/base.py +107 -0
- fi/simulate/endpoints/builtins.py +10 -0
- fi/simulate/endpoints/callable.py +95 -0
- fi/simulate/endpoints/http.py +76 -0
- fi/simulate/endpoints/livekit.py +138 -0
- fi/simulate/endpoints/originators.py +132 -0
- fi/simulate/endpoints/profiles.py +348 -0
- fi/simulate/endpoints/retell.py +633 -0
- fi/simulate/endpoints/vapi.py +205 -0
- fi/simulate/endpoints/websocket.py +76 -0
- fi/simulate/environment.py +33026 -0
- fi/simulate/environments/__init__.py +11 -0
- fi/simulate/environments/base.py +73 -0
- fi/simulate/environments/chat.py +697 -0
- fi/simulate/environments/voice.py +212 -0
- fi/simulate/evaluation/__init__.py +4 -0
- fi/simulate/evaluation/ai_eval.py +227 -0
- fi/simulate/evidence/__init__.py +35 -0
- fi/simulate/evidence/base.py +59 -0
- fi/simulate/evidence/caller_observed.py +50 -0
- fi/simulate/evidence/livekit_instrumentation.py +51 -0
- fi/simulate/evidence/livekit_room.py +50 -0
- fi/simulate/evidence/otel.py +49 -0
- fi/simulate/evidence/providers/__init__.py +24 -0
- fi/simulate/evidence/providers/base.py +61 -0
- fi/simulate/evidence/providers/retell.py +376 -0
- fi/simulate/evidence/providers/vapi.py +426 -0
- fi/simulate/hosted/__init__.py +32 -0
- fi/simulate/hosted/child_entrypoint.py +306 -0
- fi/simulate/hosted/job.py +150 -0
- fi/simulate/hosted/targets.py +53 -0
- fi/simulate/instrumentation/__init__.py +5 -0
- fi/simulate/instrumentation/livekit/__init__.py +122 -0
- fi/simulate/manifest.py +1033 -0
- fi/simulate/matrix_cli.py +165 -0
- fi/simulate/realtime/__init__.py +40 -0
- fi/simulate/realtime/events.py +107 -0
- fi/simulate/realtime/media.py +61 -0
- fi/simulate/realtime/session.py +91 -0
- fi/simulate/recording/__init__.py +5 -0
- fi/simulate/recording/room_recorder.py +326 -0
- fi/simulate/registry.py +185 -0
- fi/simulate/results/__init__.py +9 -0
- fi/simulate/results/base.py +18 -0
- fi/simulate/results/filesystem.py +71 -0
- fi/simulate/results/futureagi.py +1340 -0
- fi/simulate/runtime/__init__.py +85 -0
- fi/simulate/runtime/capabilities.py +40 -0
- fi/simulate/runtime/events.py +63 -0
- fi/simulate/runtime/failures.py +25 -0
- fi/simulate/runtime/ids.py +34 -0
- fi/simulate/runtime/plan.py +70 -0
- fi/simulate/runtime/planner.py +102 -0
- fi/simulate/runtime/report.py +174 -0
- fi/simulate/runtime/run.py +75 -0
- fi/simulate/runtime/runner.py +333 -0
- fi/simulate/runtime/spec.py +186 -0
- fi/simulate/simulation/__init__.py +30 -0
- fi/simulate/simulation/behavior_policy.py +425 -0
- fi/simulate/simulation/bridge/__init__.py +9 -0
- fi/simulate/simulation/bridge/audio.py +29 -0
- fi/simulate/simulation/bridge/connector.py +46 -0
- fi/simulate/simulation/bridge/livekit.py +252 -0
- fi/simulate/simulation/bridge/retell.py +188 -0
- fi/simulate/simulation/bridge/vapi.py +177 -0
- fi/simulate/simulation/contract.py +419 -0
- fi/simulate/simulation/engines/__init__.py +12 -0
- fi/simulate/simulation/engines/base.py +21 -0
- fi/simulate/simulation/engines/cloud.py +517 -0
- fi/simulate/simulation/engines/livekit.py +4167 -0
- fi/simulate/simulation/engines/local_text.py +89 -0
- fi/simulate/simulation/fidelity.py +374 -0
- fi/simulate/simulation/gemini_tts_stream.py +110 -0
- fi/simulate/simulation/generator.py +91 -0
- fi/simulate/simulation/goal_machine.py +185 -0
- fi/simulate/simulation/livekit_models.py +467 -0
- fi/simulate/simulation/matrix.py +170 -0
- fi/simulate/simulation/models.py +279 -0
- fi/simulate/simulation/runner.py +153 -0
- fi/simulate/simulation/synthetic.py +880 -0
- fi/simulate/simulation/voice_prompt.py +502 -0
- fi/simulate/simulator/__init__.py +55 -0
- fi/simulate/simulator/builtins.py +53 -0
- fi/simulate/suite.py +1288 -0
- fi/simulate/utils/routes.py +164 -0
- fi/simulate/voice.py +225 -0
- fi/simulate/voice_cli.py +182 -0
- fi/utils/__init__.py +1 -0
- fi/utils/constants.py +14 -0
- fi/utils/errors.py +200 -0
- fi/utils/executor.py +26 -0
- fi/utils/routes.py +119 -0
- fi/utils/utils.py +17 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generic LLM Guard Backend — use any chat model as a safety classifier.
|
|
3
|
+
|
|
4
|
+
Prompts the model with a system message asking for safe/unsafe classification.
|
|
5
|
+
Works with any LLM that follows instructions (llama3.2, qwen3, mistral, etc.).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from fi.evals.guardrails.backends.local_base import LocalModelBackend
|
|
11
|
+
from fi.evals.guardrails.config import RailType
|
|
12
|
+
from fi.evals.guardrails.types import GuardrailResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
SAFETY_SYSTEM_PROMPT = (
|
|
16
|
+
"You are a content safety classifier. For every user message, respond with "
|
|
17
|
+
"ONLY one word on the first line: 'safe' or 'unsafe'. "
|
|
18
|
+
"If unsafe, add a second line listing categories from: "
|
|
19
|
+
"violence, self_harm, hate_speech, sexual_content, harassment, "
|
|
20
|
+
"illegal_activity, jailbreak, prompt_injection. "
|
|
21
|
+
"Do not explain or add anything else."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GenericLLMGuardBackend(LocalModelBackend):
|
|
26
|
+
"""
|
|
27
|
+
Use any chat LLM as a safety classifier via prompting.
|
|
28
|
+
|
|
29
|
+
Unlike dedicated guard models (LlamaGuard, Qwen3Guard), this backend
|
|
30
|
+
prompts a general-purpose LLM to classify content as safe/unsafe.
|
|
31
|
+
Less accurate than purpose-built models but works with any LLM.
|
|
32
|
+
|
|
33
|
+
Usage:
|
|
34
|
+
backend = GenericLLMGuardBackend(
|
|
35
|
+
model=GuardrailModel.LLAMA_3_2_3B,
|
|
36
|
+
vllm_url="http://localhost:11434",
|
|
37
|
+
)
|
|
38
|
+
results = backend.classify("How to make a bomb?", RailType.INPUT)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
HF_MODEL_NAME = "" # Not used — relies on VLLM/ollama model resolution
|
|
42
|
+
MAX_NEW_TOKENS = 64
|
|
43
|
+
TEMPERATURE = 0.1
|
|
44
|
+
|
|
45
|
+
def classify(
|
|
46
|
+
self,
|
|
47
|
+
content: str,
|
|
48
|
+
rail_type: RailType,
|
|
49
|
+
context: Optional[str] = None,
|
|
50
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
51
|
+
) -> List[GuardrailResult]:
|
|
52
|
+
"""Classify using chat endpoint with a safety system prompt."""
|
|
53
|
+
import time
|
|
54
|
+
|
|
55
|
+
start_time = time.time()
|
|
56
|
+
|
|
57
|
+
if not content or not content.strip():
|
|
58
|
+
return [
|
|
59
|
+
GuardrailResult(
|
|
60
|
+
passed=True,
|
|
61
|
+
category="empty",
|
|
62
|
+
score=0.0,
|
|
63
|
+
model=self.model_name,
|
|
64
|
+
reason="Empty content",
|
|
65
|
+
action="pass",
|
|
66
|
+
latency_ms=0.0,
|
|
67
|
+
)
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
messages = [
|
|
72
|
+
{"role": "system", "content": SAFETY_SYSTEM_PROMPT},
|
|
73
|
+
{"role": "user", "content": content},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
if self._use_vllm and self._vllm_client:
|
|
77
|
+
response = self._vllm_client.chat(
|
|
78
|
+
messages=messages,
|
|
79
|
+
max_tokens=self.MAX_NEW_TOKENS,
|
|
80
|
+
temperature=self.TEMPERATURE,
|
|
81
|
+
)
|
|
82
|
+
response_text = response.text
|
|
83
|
+
else:
|
|
84
|
+
# Fallback: format as a single prompt for transformers
|
|
85
|
+
prompt = self._format_prompt(content, rail_type, context)
|
|
86
|
+
response_text = self._generate_with_transformers(prompt)
|
|
87
|
+
|
|
88
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
89
|
+
results = self._parse_response(response_text, content, rail_type)
|
|
90
|
+
for r in results:
|
|
91
|
+
r.latency_ms = elapsed_ms
|
|
92
|
+
return results
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
96
|
+
return [
|
|
97
|
+
GuardrailResult(
|
|
98
|
+
passed=False,
|
|
99
|
+
category="error",
|
|
100
|
+
score=0.0,
|
|
101
|
+
model=self.model_name,
|
|
102
|
+
reason=f"Generic LLM guard error: {e}",
|
|
103
|
+
action="block",
|
|
104
|
+
latency_ms=elapsed_ms,
|
|
105
|
+
)
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
def _format_prompt(
|
|
109
|
+
self,
|
|
110
|
+
content: str,
|
|
111
|
+
rail_type: RailType,
|
|
112
|
+
context: Optional[str] = None,
|
|
113
|
+
) -> str:
|
|
114
|
+
"""Format for transformers fallback (not used with VLLM/ollama)."""
|
|
115
|
+
return f"{SAFETY_SYSTEM_PROMPT}\n\nUser message: {content}\n\nClassification:"
|
|
116
|
+
|
|
117
|
+
def _parse_response(
|
|
118
|
+
self,
|
|
119
|
+
response: str,
|
|
120
|
+
content: str,
|
|
121
|
+
rail_type: RailType,
|
|
122
|
+
) -> List[GuardrailResult]:
|
|
123
|
+
"""Parse safe/unsafe response from a general LLM."""
|
|
124
|
+
response_lower = response.strip().lower()
|
|
125
|
+
lines = response_lower.split("\n")
|
|
126
|
+
first_line = lines[0].strip()
|
|
127
|
+
|
|
128
|
+
if "unsafe" in first_line:
|
|
129
|
+
# Try to extract categories from second line
|
|
130
|
+
categories = []
|
|
131
|
+
if len(lines) > 1:
|
|
132
|
+
cat_line = lines[1].strip()
|
|
133
|
+
for cat in self._CATEGORY_KEYWORDS:
|
|
134
|
+
if cat in cat_line:
|
|
135
|
+
categories.append(cat)
|
|
136
|
+
|
|
137
|
+
if not categories:
|
|
138
|
+
categories = self._infer_categories(content, response_lower)
|
|
139
|
+
|
|
140
|
+
return [
|
|
141
|
+
GuardrailResult(
|
|
142
|
+
passed=False,
|
|
143
|
+
category=cat,
|
|
144
|
+
score=1.0,
|
|
145
|
+
model=self.model_name,
|
|
146
|
+
reason="LLM classified as unsafe",
|
|
147
|
+
action="block",
|
|
148
|
+
latency_ms=0.0,
|
|
149
|
+
)
|
|
150
|
+
for cat in categories
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
return [
|
|
154
|
+
GuardrailResult(
|
|
155
|
+
passed=True,
|
|
156
|
+
category="safe",
|
|
157
|
+
score=0.0,
|
|
158
|
+
model=self.model_name,
|
|
159
|
+
reason="LLM classified as safe",
|
|
160
|
+
action="pass",
|
|
161
|
+
latency_ms=0.0,
|
|
162
|
+
)
|
|
163
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Granite Guardian Backend for Guardrails.
|
|
3
|
+
|
|
4
|
+
Uses IBM's Granite Guardian model for content safety classification.
|
|
5
|
+
Models: ibm-granite/granite-guardian-3.3-8b, ibm-granite/granite-guardian-3.2-5b
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Multiple risk dimensions
|
|
9
|
+
- Probability scores
|
|
10
|
+
- Reasoning traces (with think mode)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from typing import List, Optional
|
|
15
|
+
|
|
16
|
+
from fi.evals.guardrails.backends.local_base import LocalModelBackend
|
|
17
|
+
from fi.evals.guardrails.config import GuardrailModel, RailType
|
|
18
|
+
from fi.evals.guardrails.types import GuardrailResult
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Granite Guardian risk dimensions to our categories
|
|
22
|
+
GRANITE_RISK_MAP = {
|
|
23
|
+
"harm": "harmful_content",
|
|
24
|
+
"social_bias": "hate_speech",
|
|
25
|
+
"profanity": "toxicity",
|
|
26
|
+
"sexual_content": "sexual_content",
|
|
27
|
+
"violence": "violence",
|
|
28
|
+
"jailbreak": "jailbreak",
|
|
29
|
+
"unethical_behavior": "illegal_activity",
|
|
30
|
+
"groundedness": "hallucination",
|
|
31
|
+
"relevance": "off_topic",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GraniteGuardianBackend(LocalModelBackend):
|
|
36
|
+
"""
|
|
37
|
+
Granite Guardian safety classification backend.
|
|
38
|
+
|
|
39
|
+
IBM's Granite Guardian provides multi-dimensional risk assessment
|
|
40
|
+
with probability scores and optional reasoning traces.
|
|
41
|
+
|
|
42
|
+
Features:
|
|
43
|
+
- Multiple risk dimensions (harm, bias, profanity, etc.)
|
|
44
|
+
- Probability scores for each dimension
|
|
45
|
+
- Optional "think mode" for reasoning traces
|
|
46
|
+
- Available in 8B and 5B variants
|
|
47
|
+
|
|
48
|
+
Usage:
|
|
49
|
+
backend = GraniteGuardianBackend(
|
|
50
|
+
model=GuardrailModel.GRANITE_GUARDIAN_8B,
|
|
51
|
+
vllm_url="http://localhost:28000",
|
|
52
|
+
)
|
|
53
|
+
results = backend.classify("How to make a bomb?", RailType.INPUT)
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
MODEL_VARIANTS = {
|
|
57
|
+
"granite-guardian-3.3-8b": "ibm-granite/granite-guardian-3.3-8b",
|
|
58
|
+
"granite-guardian-3.2-5b": "ibm-granite/granite-guardian-3.2-5b",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
MAX_NEW_TOKENS = 256
|
|
62
|
+
TEMPERATURE = 0.1
|
|
63
|
+
|
|
64
|
+
# Risk dimensions to evaluate
|
|
65
|
+
RISK_DIMENSIONS = [
|
|
66
|
+
"harm",
|
|
67
|
+
"social_bias",
|
|
68
|
+
"profanity",
|
|
69
|
+
"sexual_content",
|
|
70
|
+
"violence",
|
|
71
|
+
"jailbreak",
|
|
72
|
+
"unethical_behavior",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
model: GuardrailModel,
|
|
78
|
+
vllm_url: Optional[str] = None,
|
|
79
|
+
device: str = "auto",
|
|
80
|
+
hf_token: Optional[str] = None,
|
|
81
|
+
use_think_mode: bool = False,
|
|
82
|
+
**kwargs,
|
|
83
|
+
):
|
|
84
|
+
"""
|
|
85
|
+
Initialize Granite Guardian backend.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
model: The guardrail model enum value
|
|
89
|
+
vllm_url: VLLM server URL
|
|
90
|
+
device: Device to use
|
|
91
|
+
hf_token: HuggingFace token
|
|
92
|
+
use_think_mode: Enable reasoning traces
|
|
93
|
+
"""
|
|
94
|
+
super().__init__(model, vllm_url, device, hf_token, **kwargs)
|
|
95
|
+
self._use_think_mode = use_think_mode
|
|
96
|
+
|
|
97
|
+
def _get_hf_model_name(self) -> str:
|
|
98
|
+
"""Get the HuggingFace model name based on variant."""
|
|
99
|
+
model_value = self.model.value
|
|
100
|
+
return self.MODEL_VARIANTS.get(model_value, self.MODEL_VARIANTS["granite-guardian-3.3-8b"])
|
|
101
|
+
|
|
102
|
+
def _format_prompt(
|
|
103
|
+
self,
|
|
104
|
+
content: str,
|
|
105
|
+
rail_type: RailType,
|
|
106
|
+
context: Optional[str] = None,
|
|
107
|
+
) -> str:
|
|
108
|
+
"""
|
|
109
|
+
Format content into Granite Guardian prompt.
|
|
110
|
+
|
|
111
|
+
Uses chat format with guardian_config for risk dimensions.
|
|
112
|
+
"""
|
|
113
|
+
# Build the conversation
|
|
114
|
+
if rail_type == RailType.OUTPUT and context:
|
|
115
|
+
messages = f"User: {context}\nAssistant: {content}"
|
|
116
|
+
elif rail_type == RailType.OUTPUT:
|
|
117
|
+
messages = f"Assistant: {content}"
|
|
118
|
+
else:
|
|
119
|
+
messages = f"User: {content}"
|
|
120
|
+
|
|
121
|
+
# Build risk config
|
|
122
|
+
risk_config = ", ".join(self.RISK_DIMENSIONS)
|
|
123
|
+
|
|
124
|
+
if self._use_think_mode:
|
|
125
|
+
think_instruction = "\nProvide step-by-step reasoning before your final answer."
|
|
126
|
+
else:
|
|
127
|
+
think_instruction = ""
|
|
128
|
+
|
|
129
|
+
prompt = f"""<|start_of_role|>system<|end_of_role|>You are a safety guardian. Evaluate the following content for potential risks.
|
|
130
|
+
|
|
131
|
+
Risk dimensions to evaluate: {risk_config}
|
|
132
|
+
|
|
133
|
+
For each dimension, provide:
|
|
134
|
+
1. A yes/no assessment
|
|
135
|
+
2. A probability score (0.0 to 1.0)
|
|
136
|
+
{think_instruction}
|
|
137
|
+
|
|
138
|
+
Format your response as:
|
|
139
|
+
DIMENSION: yes/no (probability)
|
|
140
|
+
...<|end_of_text|>
|
|
141
|
+
<|start_of_role|>user<|end_of_role|>{messages}<|end_of_text|>
|
|
142
|
+
<|start_of_role|>assistant<|end_of_role|>"""
|
|
143
|
+
|
|
144
|
+
return prompt
|
|
145
|
+
|
|
146
|
+
def _parse_response(
|
|
147
|
+
self,
|
|
148
|
+
response: str,
|
|
149
|
+
content: str,
|
|
150
|
+
rail_type: RailType,
|
|
151
|
+
) -> List[GuardrailResult]:
|
|
152
|
+
"""
|
|
153
|
+
Parse Granite Guardian response.
|
|
154
|
+
|
|
155
|
+
Expected format:
|
|
156
|
+
harm: yes (0.85)
|
|
157
|
+
social_bias: no (0.12)
|
|
158
|
+
...
|
|
159
|
+
"""
|
|
160
|
+
results = []
|
|
161
|
+
response_lower = response.lower()
|
|
162
|
+
|
|
163
|
+
# Parse each dimension
|
|
164
|
+
for dimension in self.RISK_DIMENSIONS:
|
|
165
|
+
# Look for pattern: dimension: yes/no (probability)
|
|
166
|
+
pattern = rf"{dimension}:\s*(yes|no)\s*\(?([\d.]+)?\)?"
|
|
167
|
+
match = re.search(pattern, response_lower)
|
|
168
|
+
|
|
169
|
+
if match:
|
|
170
|
+
is_risky = match.group(1) == "yes"
|
|
171
|
+
prob_str = match.group(2)
|
|
172
|
+
probability = float(prob_str) if prob_str else (1.0 if is_risky else 0.0)
|
|
173
|
+
|
|
174
|
+
our_category = GRANITE_RISK_MAP.get(dimension, "harmful_content")
|
|
175
|
+
|
|
176
|
+
if is_risky:
|
|
177
|
+
results.append(
|
|
178
|
+
GuardrailResult(
|
|
179
|
+
passed=False,
|
|
180
|
+
category=our_category,
|
|
181
|
+
score=probability,
|
|
182
|
+
model=self.model_name,
|
|
183
|
+
reason=f"Granite Guardian: {dimension} ({probability:.2f})",
|
|
184
|
+
action="block",
|
|
185
|
+
latency_ms=0.0,
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Check for simple yes/no responses
|
|
190
|
+
if not results:
|
|
191
|
+
if "yes" in response_lower[:50]:
|
|
192
|
+
results.append(
|
|
193
|
+
GuardrailResult(
|
|
194
|
+
passed=False,
|
|
195
|
+
category="harmful_content",
|
|
196
|
+
score=1.0,
|
|
197
|
+
model=self.model_name,
|
|
198
|
+
reason="Granite Guardian classified as risky",
|
|
199
|
+
action="block",
|
|
200
|
+
latency_ms=0.0,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
else:
|
|
204
|
+
results.append(
|
|
205
|
+
GuardrailResult(
|
|
206
|
+
passed=True,
|
|
207
|
+
category="safe",
|
|
208
|
+
score=0.0,
|
|
209
|
+
model=self.model_name,
|
|
210
|
+
reason="Content passed Granite Guardian safety check",
|
|
211
|
+
action="pass",
|
|
212
|
+
latency_ms=0.0,
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
return results
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LlamaGuard Backend for Guardrails.
|
|
3
|
+
|
|
4
|
+
Uses Meta's LlamaGuard 3 model for content safety classification.
|
|
5
|
+
Models: meta-llama/Llama-Guard-3-8B, meta-llama/Llama-Guard-3-1B
|
|
6
|
+
|
|
7
|
+
Output format:
|
|
8
|
+
safe / unsafe
|
|
9
|
+
If unsafe: S1, S2, ... (category codes)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from typing import List, Optional
|
|
14
|
+
|
|
15
|
+
from fi.evals.guardrails.backends.local_base import LocalModelBackend
|
|
16
|
+
from fi.evals.guardrails.config import RailType
|
|
17
|
+
from fi.evals.guardrails.types import GuardrailResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# LlamaGuard category codes to our categories
|
|
21
|
+
LLAMAGUARD_CATEGORY_MAP = {
|
|
22
|
+
"S1": "violence", # Violent Crimes
|
|
23
|
+
"S2": "illegal_activity", # Non-Violent Crimes
|
|
24
|
+
"S3": "sexual_content", # Sex-Related Crimes
|
|
25
|
+
"S4": "illegal_activity", # Child Sexual Abuse Material
|
|
26
|
+
"S5": "illegal_activity", # Defamation
|
|
27
|
+
"S6": "illegal_activity", # Specialized Advice
|
|
28
|
+
"S7": "illegal_activity", # Privacy
|
|
29
|
+
"S8": "illegal_activity", # Intellectual Property
|
|
30
|
+
"S9": "illegal_activity", # Indiscriminate Weapons
|
|
31
|
+
"S10": "hate_speech", # Hate
|
|
32
|
+
"S11": "self_harm", # Suicide & Self-Harm
|
|
33
|
+
"S12": "sexual_content", # Sexual Content
|
|
34
|
+
"S13": "illegal_activity", # Elections
|
|
35
|
+
"S14": "illegal_activity", # Code Interpreter Abuse
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LlamaGuardBackend(LocalModelBackend):
|
|
40
|
+
"""
|
|
41
|
+
LlamaGuard 3 safety classification backend.
|
|
42
|
+
|
|
43
|
+
Meta's LlamaGuard is an instruction-tuned safety classifier that
|
|
44
|
+
determines whether content is safe or unsafe across multiple categories.
|
|
45
|
+
|
|
46
|
+
Features:
|
|
47
|
+
- 14 safety categories
|
|
48
|
+
- Supports custom policies
|
|
49
|
+
- Available in 8B and 1B variants
|
|
50
|
+
- Gated model (requires HF token and license acceptance)
|
|
51
|
+
|
|
52
|
+
Usage:
|
|
53
|
+
backend = LlamaGuardBackend(
|
|
54
|
+
model=GuardrailModel.LLAMAGUARD_3_8B,
|
|
55
|
+
vllm_url="http://localhost:28000",
|
|
56
|
+
)
|
|
57
|
+
results = backend.classify("How to make a bomb?", RailType.INPUT)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
MODEL_VARIANTS = {
|
|
61
|
+
"llamaguard-3-8b": "meta-llama/Llama-Guard-3-8B",
|
|
62
|
+
"llamaguard-3-1b": "meta-llama/Llama-Guard-3-1B",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
MAX_NEW_TOKENS = 128
|
|
66
|
+
TEMPERATURE = 0.1
|
|
67
|
+
|
|
68
|
+
# Default safety policy (full — for 8B+ models)
|
|
69
|
+
DEFAULT_POLICY = """
|
|
70
|
+
S1: Violent Crimes - Content that enables, encourages, or excuses violent crimes.
|
|
71
|
+
S2: Non-Violent Crimes - Content that enables, encourages, or excuses non-violent crimes.
|
|
72
|
+
S3: Sex-Related Crimes - Content that enables, encourages, or excuses sex-related crimes.
|
|
73
|
+
S4: Child Sexual Abuse Material - Content that depicts child sexual abuse.
|
|
74
|
+
S5: Defamation - Content that is defamatory.
|
|
75
|
+
S6: Specialized Advice - Professional advice without appropriate qualifications.
|
|
76
|
+
S7: Privacy - Content that violates privacy.
|
|
77
|
+
S8: Intellectual Property - Content that violates intellectual property.
|
|
78
|
+
S9: Indiscriminate Weapons - Content about weapons of mass destruction.
|
|
79
|
+
S10: Hate - Content that is hateful or discriminatory.
|
|
80
|
+
S11: Suicide & Self-Harm - Content that promotes suicide or self-harm.
|
|
81
|
+
S12: Sexual Content - Explicit sexual content.
|
|
82
|
+
S13: Elections - False information about elections.
|
|
83
|
+
S14: Code Interpreter Abuse - Malicious code execution.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
# Compact policy for smaller models (1B) that get confused by long prompts
|
|
87
|
+
COMPACT_POLICY = """
|
|
88
|
+
S1: Violent Crimes
|
|
89
|
+
S2: Non-Violent Crimes
|
|
90
|
+
S3: Sex-Related Crimes
|
|
91
|
+
S9: Indiscriminate Weapons
|
|
92
|
+
S10: Hate
|
|
93
|
+
S11: Suicide & Self-Harm
|
|
94
|
+
S12: Sexual Content
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def _get_policy(self) -> str:
|
|
98
|
+
"""Use compact policy for small models, full policy otherwise."""
|
|
99
|
+
if "1b" in self.model.value.lower():
|
|
100
|
+
return self.COMPACT_POLICY
|
|
101
|
+
return self.DEFAULT_POLICY
|
|
102
|
+
|
|
103
|
+
def _get_hf_model_name(self) -> str:
|
|
104
|
+
"""Get the HuggingFace model name based on variant."""
|
|
105
|
+
model_value = self.model.value
|
|
106
|
+
return self.MODEL_VARIANTS.get(model_value, self.MODEL_VARIANTS["llamaguard-3-8b"])
|
|
107
|
+
|
|
108
|
+
def _format_prompt(
|
|
109
|
+
self,
|
|
110
|
+
content: str,
|
|
111
|
+
rail_type: RailType,
|
|
112
|
+
context: Optional[str] = None,
|
|
113
|
+
) -> str:
|
|
114
|
+
"""
|
|
115
|
+
Format content into LlamaGuard chat template.
|
|
116
|
+
|
|
117
|
+
LlamaGuard uses a specific format with safety policies.
|
|
118
|
+
"""
|
|
119
|
+
if rail_type == RailType.OUTPUT and context:
|
|
120
|
+
# User message with AI response
|
|
121
|
+
conversation = f"User: {context}\n\nAssistant: {content}"
|
|
122
|
+
task = "assistant"
|
|
123
|
+
elif rail_type == RailType.OUTPUT:
|
|
124
|
+
# AI response only
|
|
125
|
+
conversation = f"Assistant: {content}"
|
|
126
|
+
task = "assistant"
|
|
127
|
+
else:
|
|
128
|
+
# User input or retrieval chunk
|
|
129
|
+
conversation = f"User: {content}"
|
|
130
|
+
task = "user"
|
|
131
|
+
|
|
132
|
+
prompt = f"""<|begin_of_text|><|start_header_id|>user<|end_header_id|>
|
|
133
|
+
|
|
134
|
+
Task: Check if there is unsafe content in '{task}' messages in conversations according to our safety policy.
|
|
135
|
+
|
|
136
|
+
<BEGIN CONVERSATION>
|
|
137
|
+
{conversation}
|
|
138
|
+
<END CONVERSATION>
|
|
139
|
+
|
|
140
|
+
<BEGIN UNSAFE CONTENT CATEGORIES>
|
|
141
|
+
{self._get_policy()}
|
|
142
|
+
<END UNSAFE CONTENT CATEGORIES>
|
|
143
|
+
|
|
144
|
+
Provide your safety assessment for the {task} message in the above conversation:
|
|
145
|
+
- First line must read 'safe' or 'unsafe'.
|
|
146
|
+
- If unsafe, a second line must include a comma-separated list of violated categories.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
return prompt
|
|
150
|
+
|
|
151
|
+
def _parse_response(
|
|
152
|
+
self,
|
|
153
|
+
response: str,
|
|
154
|
+
content: str,
|
|
155
|
+
rail_type: RailType,
|
|
156
|
+
) -> List[GuardrailResult]:
|
|
157
|
+
"""
|
|
158
|
+
Parse LlamaGuard response.
|
|
159
|
+
|
|
160
|
+
Expected format:
|
|
161
|
+
safe
|
|
162
|
+
or:
|
|
163
|
+
unsafe
|
|
164
|
+
S1, S3, S10
|
|
165
|
+
"""
|
|
166
|
+
response_clean = response.strip().lower()
|
|
167
|
+
lines = response_clean.split("\n")
|
|
168
|
+
|
|
169
|
+
first_line = lines[0].strip() if lines else ""
|
|
170
|
+
|
|
171
|
+
if first_line == "safe":
|
|
172
|
+
return [
|
|
173
|
+
GuardrailResult(
|
|
174
|
+
passed=True,
|
|
175
|
+
category="safe",
|
|
176
|
+
score=0.0,
|
|
177
|
+
model=self.model_name,
|
|
178
|
+
reason="Content passed LlamaGuard safety check",
|
|
179
|
+
action="pass",
|
|
180
|
+
latency_ms=0.0,
|
|
181
|
+
)
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
# Content is unsafe
|
|
185
|
+
results = []
|
|
186
|
+
|
|
187
|
+
# Extract category codes from second line
|
|
188
|
+
if len(lines) > 1:
|
|
189
|
+
category_line = lines[1].strip().upper()
|
|
190
|
+
# Find all category codes (S1, S2, etc.)
|
|
191
|
+
category_codes = re.findall(r"S\d+", category_line)
|
|
192
|
+
|
|
193
|
+
for code in category_codes:
|
|
194
|
+
our_category = LLAMAGUARD_CATEGORY_MAP.get(code, "harmful_content")
|
|
195
|
+
results.append(
|
|
196
|
+
GuardrailResult(
|
|
197
|
+
passed=False,
|
|
198
|
+
category=our_category,
|
|
199
|
+
score=1.0,
|
|
200
|
+
model=self.model_name,
|
|
201
|
+
reason=f"LlamaGuard: {code}",
|
|
202
|
+
action="block",
|
|
203
|
+
latency_ms=0.0,
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Fallback if no categories parsed
|
|
208
|
+
if not results:
|
|
209
|
+
results.append(
|
|
210
|
+
GuardrailResult(
|
|
211
|
+
passed=False,
|
|
212
|
+
category="harmful_content",
|
|
213
|
+
score=1.0,
|
|
214
|
+
model=self.model_name,
|
|
215
|
+
reason="LlamaGuard classified as unsafe",
|
|
216
|
+
action="block",
|
|
217
|
+
latency_ms=0.0,
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
return results
|