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,522 @@
|
|
|
1
|
+
"""Persona authoring + calibration lifecycle (Phase 7, unit 4).
|
|
2
|
+
|
|
3
|
+
Lifecycle = ``sampled -> validated -> interrogated -> admitted`` (ARCH §4
|
|
4
|
+
canon). Interrogation is the PICon battery — probe legs ``internal`` /
|
|
5
|
+
``external`` / ``retest`` — run against a SCRIPTED deterministic responder
|
|
6
|
+
(no LLM, no keys, no network). The retest leg is replay-based: the identical
|
|
7
|
+
battery re-runs ``repeats`` times under the same seed and the realization
|
|
8
|
+
vectors must agree fork-free (``_probe_divergence_step`` re-implements the
|
|
9
|
+
``live/_stats.divergence_step`` SEMANTIC — the live package is never imported
|
|
10
|
+
here, per the live_lane_boundary rule).
|
|
11
|
+
|
|
12
|
+
Emits ``agent-learning.persona-calibration.v1`` artifacts; bias-lint results
|
|
13
|
+
ride INSIDE them as a ``bias_lint`` block (never a standalone kind).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
import random
|
|
21
|
+
from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union
|
|
22
|
+
|
|
23
|
+
from fi.simulate.simulation.behavior_policy import (
|
|
24
|
+
BEHAVIOR_POLICY_AXIS_FIELDS,
|
|
25
|
+
PERSONA_BEHAVIOR_AXES,
|
|
26
|
+
PERSONA_BEHAVIOR_REALIZATION_METRICS,
|
|
27
|
+
compile_behavior_policy,
|
|
28
|
+
)
|
|
29
|
+
from fi.simulate.simulation.models import (
|
|
30
|
+
AttackConditioning,
|
|
31
|
+
BehaviorPolicy,
|
|
32
|
+
Persona,
|
|
33
|
+
PersonaFact,
|
|
34
|
+
PersonaIdentity,
|
|
35
|
+
PersonaProvenance,
|
|
36
|
+
PersonaTemperament,
|
|
37
|
+
Scenario,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
from ._library import (
|
|
41
|
+
PERSONA_CALIBRATION_KIND,
|
|
42
|
+
save_calibration,
|
|
43
|
+
)
|
|
44
|
+
from ._upgrade import upgrade_legacy_persona
|
|
45
|
+
|
|
46
|
+
PERSONA_CALIBRATION_STAGES = ("sampled", "validated", "interrogated", "admitted")
|
|
47
|
+
PERSONA_CALIBRATION_PROBES = ("internal", "external", "retest")
|
|
48
|
+
|
|
49
|
+
# Monotone upgrade lattice: calibration may only move a persona UP this rank.
|
|
50
|
+
# cloud_downloaded and trace_mined are provenance facts, never calibration
|
|
51
|
+
# outcomes — calibration stamps calibrated=True but leaves their class alone.
|
|
52
|
+
_CALIBRATABLE_RANK = {
|
|
53
|
+
"legacy": 0,
|
|
54
|
+
"hand_written": 1,
|
|
55
|
+
"schema_sampled": 2,
|
|
56
|
+
"policy_evolved": 3,
|
|
57
|
+
}
|
|
58
|
+
_CALIBRATION_TARGETS = ("hand_written", "schema_sampled", "policy_evolved")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def build_persona(
|
|
62
|
+
*,
|
|
63
|
+
name: str,
|
|
64
|
+
situation: str,
|
|
65
|
+
outcome: str,
|
|
66
|
+
role: Optional[str] = None,
|
|
67
|
+
summary: Optional[str] = None,
|
|
68
|
+
language: Optional[str] = None,
|
|
69
|
+
demographics: Optional[Mapping[str, Any]] = None,
|
|
70
|
+
style_notes: Sequence[str] = (),
|
|
71
|
+
temperament: Union[PersonaTemperament, Mapping[str, float], None] = None,
|
|
72
|
+
behavior_policy: Union[BehaviorPolicy, Mapping[str, Any], None] = None,
|
|
73
|
+
knowledge: Sequence[Union[PersonaFact, Mapping[str, Any]]] = (),
|
|
74
|
+
attack: Union[AttackConditioning, Mapping[str, Any], None] = None,
|
|
75
|
+
evidence_class: str = "hand_written",
|
|
76
|
+
) -> Persona:
|
|
77
|
+
"""Deterministic persona writer (hand_written / schema_sampled classes).
|
|
78
|
+
|
|
79
|
+
Same inputs -> byte-identical persona (content hash stable). When a
|
|
80
|
+
temperament is given without an explicit policy, the engine compiler
|
|
81
|
+
derives layer 3 (explicit beats derived, ARCH Decision 4)."""
|
|
82
|
+
if evidence_class not in ("hand_written", "schema_sampled"):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"build_persona writes hand_written or schema_sampled personas "
|
|
85
|
+
f"only (got {evidence_class!r}); other classes have their own "
|
|
86
|
+
"writers (calibration / pull / upgrade)"
|
|
87
|
+
)
|
|
88
|
+
temperament_model: Optional[PersonaTemperament] = None
|
|
89
|
+
if temperament is not None:
|
|
90
|
+
temperament_model = (
|
|
91
|
+
temperament if isinstance(temperament, PersonaTemperament)
|
|
92
|
+
else PersonaTemperament(**dict(temperament))
|
|
93
|
+
)
|
|
94
|
+
policy_model: Optional[BehaviorPolicy] = None
|
|
95
|
+
if behavior_policy is not None:
|
|
96
|
+
policy_model = (
|
|
97
|
+
behavior_policy if isinstance(behavior_policy, BehaviorPolicy)
|
|
98
|
+
else BehaviorPolicy(**dict(behavior_policy))
|
|
99
|
+
)
|
|
100
|
+
facts = [
|
|
101
|
+
fact if isinstance(fact, PersonaFact) else PersonaFact(**dict(fact))
|
|
102
|
+
for fact in knowledge
|
|
103
|
+
]
|
|
104
|
+
attack_model: Optional[AttackConditioning] = None
|
|
105
|
+
if attack is not None:
|
|
106
|
+
attack_model = (
|
|
107
|
+
attack if isinstance(attack, AttackConditioning)
|
|
108
|
+
else AttackConditioning(**dict(attack))
|
|
109
|
+
)
|
|
110
|
+
embedded: Dict[str, Any] = {"name": name}
|
|
111
|
+
if role:
|
|
112
|
+
embedded["role"] = role
|
|
113
|
+
persona = Persona(
|
|
114
|
+
persona=embedded,
|
|
115
|
+
situation=situation,
|
|
116
|
+
outcome=outcome,
|
|
117
|
+
identity=PersonaIdentity(
|
|
118
|
+
name=name,
|
|
119
|
+
role=role,
|
|
120
|
+
summary=summary,
|
|
121
|
+
language=language,
|
|
122
|
+
demographics=dict(demographics or {}),
|
|
123
|
+
style_notes=list(style_notes),
|
|
124
|
+
),
|
|
125
|
+
temperament=temperament_model,
|
|
126
|
+
behavior_policy=policy_model,
|
|
127
|
+
knowledge=facts,
|
|
128
|
+
attack=attack_model,
|
|
129
|
+
provenance=PersonaProvenance(evidence_class=evidence_class),
|
|
130
|
+
)
|
|
131
|
+
if persona.behavior_policy is None and temperament_model is not None:
|
|
132
|
+
compiled = compile_behavior_policy(persona)
|
|
133
|
+
persona = _rebuild(persona, behavior_policy=compiled)
|
|
134
|
+
return persona
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _rebuild(persona: Persona, **updates: Any) -> Persona:
|
|
138
|
+
"""Re-validate after an update so the content-address version re-stamps."""
|
|
139
|
+
payload = persona.model_dump(exclude={"version"}, exclude_none=True)
|
|
140
|
+
for key, value in updates.items():
|
|
141
|
+
if value is None:
|
|
142
|
+
payload.pop(key, None)
|
|
143
|
+
else:
|
|
144
|
+
payload[key] = value
|
|
145
|
+
return Persona(**payload)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _taxonomy() -> Tuple[List[str], List[str]]:
|
|
149
|
+
# Lazy read of the EXISTING gate-enforced 10x6 taxonomy constants —
|
|
150
|
+
# facade-side membership check (the engine never imports trinity).
|
|
151
|
+
from fi.alk import trinity
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
list(trinity.V1_REDTEAM_RESEARCH_ATTACK_TYPES),
|
|
155
|
+
list(trinity.V1_REDTEAM_RESEARCH_SURFACES),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def validate_persona(persona_or_row: Union[Persona, Mapping[str, Any]]) -> Dict[str, Any]:
|
|
160
|
+
"""SPASM-style schema + realization-metric validation (UI §2.1 shape)."""
|
|
161
|
+
findings: List[Dict[str, Any]] = []
|
|
162
|
+
checks: Dict[str, str] = {}
|
|
163
|
+
try:
|
|
164
|
+
persona = upgrade_legacy_persona(persona_or_row)
|
|
165
|
+
checks["schema"] = "pass"
|
|
166
|
+
except Exception as exc: # noqa: BLE001 — structured refusal, never a traceback
|
|
167
|
+
return {
|
|
168
|
+
"status": "invalid",
|
|
169
|
+
"exit_code": 1,
|
|
170
|
+
"checks": {"schema": "fail"},
|
|
171
|
+
"findings": [{
|
|
172
|
+
"type": "persona_schema_invalid",
|
|
173
|
+
"level": "error",
|
|
174
|
+
"reason": str(exc),
|
|
175
|
+
}],
|
|
176
|
+
"representativeness_claim": "none",
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# every behavior axis carries its canon-paired metric (doctrine #7);
|
|
180
|
+
# the pairing is structural: 1:1, ordered, and policy fields exist.
|
|
181
|
+
pairing_ok = (
|
|
182
|
+
len(PERSONA_BEHAVIOR_AXES) == len(PERSONA_BEHAVIOR_REALIZATION_METRICS) == 6
|
|
183
|
+
and all(
|
|
184
|
+
axis == pair[0] and pair[1] in BehaviorPolicy.model_fields
|
|
185
|
+
for axis, pair in zip(PERSONA_BEHAVIOR_AXES, BEHAVIOR_POLICY_AXIS_FIELDS)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
checks["realization_metrics_per_axis"] = "pass" if pairing_ok else "fail"
|
|
189
|
+
if not pairing_ok:
|
|
190
|
+
findings.append({
|
|
191
|
+
"type": "persona_axis_unobservable",
|
|
192
|
+
"level": "error",
|
|
193
|
+
"reason": "a behavior axis lost its canon realization-metric pairing",
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
first = compile_behavior_policy(persona)
|
|
198
|
+
second = compile_behavior_policy(persona)
|
|
199
|
+
checks["policy_compiles"] = (
|
|
200
|
+
"pass" if first.model_dump() == second.model_dump() else "fail"
|
|
201
|
+
)
|
|
202
|
+
except Exception as exc: # noqa: BLE001
|
|
203
|
+
checks["policy_compiles"] = "fail"
|
|
204
|
+
findings.append({
|
|
205
|
+
"type": "persona_policy_compile_failed",
|
|
206
|
+
"level": "error",
|
|
207
|
+
"reason": str(exc),
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
# the persona never owns the task (2601.15290 separation)
|
|
211
|
+
owns_task = any(key in persona.persona for key in ("goal", "goals", "task"))
|
|
212
|
+
checks["goals_binding_is_scenario_scoped"] = "fail" if owns_task else "pass"
|
|
213
|
+
if owns_task:
|
|
214
|
+
findings.append({
|
|
215
|
+
"type": "persona_owns_task",
|
|
216
|
+
"level": "error",
|
|
217
|
+
"reason": "goals belong to the Scenario, never the Persona",
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
demographics_present = bool(
|
|
221
|
+
persona.identity is not None and persona.identity.demographics
|
|
222
|
+
)
|
|
223
|
+
checks["demographics"] = "flagged" if demographics_present else "absent"
|
|
224
|
+
if demographics_present:
|
|
225
|
+
findings.append({
|
|
226
|
+
"type": "persona_demographics_flagged",
|
|
227
|
+
"level": "info",
|
|
228
|
+
"reason": (
|
|
229
|
+
"demographic fields flag this persona for set-level bias "
|
|
230
|
+
"lint; admit is blocked until the lint passes (P7-D4)"
|
|
231
|
+
),
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
if persona.attack is not None:
|
|
235
|
+
attack_types, surfaces = _taxonomy()
|
|
236
|
+
bad_strategies = sorted(set(persona.attack.strategies) - set(attack_types))
|
|
237
|
+
bad_surfaces = sorted(set(persona.attack.surfaces) - set(surfaces))
|
|
238
|
+
checks["attack_taxonomy"] = "fail" if (bad_strategies or bad_surfaces) else "pass"
|
|
239
|
+
if bad_strategies or bad_surfaces:
|
|
240
|
+
findings.append({
|
|
241
|
+
"type": "persona_attack_taxonomy_violation",
|
|
242
|
+
"level": "error",
|
|
243
|
+
"reason": (
|
|
244
|
+
f"strategies {bad_strategies} / surfaces {bad_surfaces} "
|
|
245
|
+
"are outside the gate-enforced 10x6 taxonomy"
|
|
246
|
+
),
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
failed = any(value == "fail" for value in checks.values())
|
|
250
|
+
return {
|
|
251
|
+
"status": "invalid" if failed else "valid",
|
|
252
|
+
"exit_code": 1 if failed else 0,
|
|
253
|
+
"checks": checks,
|
|
254
|
+
"findings": findings,
|
|
255
|
+
"representativeness_claim": "none",
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def validate_scenario(scenario: Scenario) -> Dict[str, Any]:
|
|
260
|
+
"""Typed-scenario validation: kind contract + adversarial taxonomy."""
|
|
261
|
+
findings: List[Dict[str, Any]] = []
|
|
262
|
+
checks: Dict[str, str] = {"schema": "pass"}
|
|
263
|
+
if scenario.kind == "adversarial":
|
|
264
|
+
attack_types, surfaces = _taxonomy()
|
|
265
|
+
ok = scenario.attack_type in attack_types and scenario.attack_surface in surfaces
|
|
266
|
+
checks["attack_taxonomy"] = "pass" if ok else "fail"
|
|
267
|
+
if not ok:
|
|
268
|
+
findings.append({
|
|
269
|
+
"type": "scenario_attack_taxonomy_violation",
|
|
270
|
+
"level": "error",
|
|
271
|
+
"reason": (
|
|
272
|
+
f"attack_type={scenario.attack_type!r} / "
|
|
273
|
+
f"attack_surface={scenario.attack_surface!r} outside the "
|
|
274
|
+
"gate-enforced 10x6 taxonomy"
|
|
275
|
+
),
|
|
276
|
+
})
|
|
277
|
+
failed = any(value == "fail" for value in checks.values())
|
|
278
|
+
return {
|
|
279
|
+
"status": "invalid" if failed else "valid",
|
|
280
|
+
"exit_code": 1 if failed else 0,
|
|
281
|
+
"checks": checks,
|
|
282
|
+
"findings": findings,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# ---------------------------------------------------------------------------
|
|
287
|
+
# PICon interrogation battery — deterministic scripted simulator
|
|
288
|
+
# ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
def _scripted_answer(
|
|
291
|
+
persona: Persona,
|
|
292
|
+
fact: PersonaFact,
|
|
293
|
+
*,
|
|
294
|
+
seed: int,
|
|
295
|
+
repeat: int,
|
|
296
|
+
) -> str:
|
|
297
|
+
"""The scripted responder: answers fact probes from the knowledge store.
|
|
298
|
+
|
|
299
|
+
``retest_jitter`` in the embedded dict simulates a persona whose conduct
|
|
300
|
+
is NOT seed-stable (per-repeat token) — the designed-to-fail retest
|
|
301
|
+
fixture; within one battery run the answer is stable, across repeats it
|
|
302
|
+
forks."""
|
|
303
|
+
if fact.disclosure == "withhold":
|
|
304
|
+
return "(withheld)"
|
|
305
|
+
answer = fact.value
|
|
306
|
+
if persona.persona.get("retest_jitter"):
|
|
307
|
+
token = random.Random(f"{seed + repeat}:{fact.key}").random()
|
|
308
|
+
answer = f"{answer} #{token:.6f}"
|
|
309
|
+
return answer
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _battery_once(
|
|
313
|
+
persona: Persona,
|
|
314
|
+
scenario: Optional[Scenario],
|
|
315
|
+
*,
|
|
316
|
+
seed: int,
|
|
317
|
+
repeat: int = 0,
|
|
318
|
+
) -> Dict[str, Any]:
|
|
319
|
+
internal_probes: List[Dict[str, Any]] = []
|
|
320
|
+
contradictions = 0
|
|
321
|
+
seen_by_key: Dict[str, str] = {}
|
|
322
|
+
for fact in persona.knowledge:
|
|
323
|
+
first = _scripted_answer(persona, fact, seed=seed, repeat=repeat)
|
|
324
|
+
paraphrase = _scripted_answer(persona, fact, seed=seed, repeat=repeat)
|
|
325
|
+
consistent = first == paraphrase
|
|
326
|
+
if fact.key in seen_by_key and seen_by_key[fact.key] != fact.value:
|
|
327
|
+
consistent = False
|
|
328
|
+
contradictions += 1
|
|
329
|
+
seen_by_key.setdefault(fact.key, fact.value)
|
|
330
|
+
internal_probes.append({
|
|
331
|
+
"key": fact.key,
|
|
332
|
+
"ask": first,
|
|
333
|
+
"paraphrase": paraphrase,
|
|
334
|
+
"consistent": consistent,
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
reality_breaks = 0
|
|
338
|
+
external_probes: List[Dict[str, Any]] = []
|
|
339
|
+
observable = (
|
|
340
|
+
dict(scenario.constraints.observable_state)
|
|
341
|
+
if scenario is not None and scenario.constraints is not None else {}
|
|
342
|
+
)
|
|
343
|
+
allowed_keys = (
|
|
344
|
+
set(scenario.constraints.max_user_knowledge)
|
|
345
|
+
if scenario is not None and scenario.constraints is not None
|
|
346
|
+
and scenario.constraints.max_user_knowledge else None
|
|
347
|
+
)
|
|
348
|
+
for fact in persona.knowledge:
|
|
349
|
+
breaks = False
|
|
350
|
+
if fact.key in observable and str(observable[fact.key]) != fact.value:
|
|
351
|
+
breaks = True # contradicts declared world facts
|
|
352
|
+
if allowed_keys is not None and fact.key not in allowed_keys:
|
|
353
|
+
breaks = True # knows state the scenario declares unobservable
|
|
354
|
+
if breaks:
|
|
355
|
+
reality_breaks += 1
|
|
356
|
+
external_probes.append({"key": fact.key, "reality_break": breaks})
|
|
357
|
+
|
|
358
|
+
probe_count = max(1, len(persona.knowledge))
|
|
359
|
+
return {
|
|
360
|
+
"internal": {
|
|
361
|
+
"score": round(1.0 - contradictions / probe_count, 6),
|
|
362
|
+
"probes": len(internal_probes),
|
|
363
|
+
"contradictions": contradictions,
|
|
364
|
+
},
|
|
365
|
+
"external": {
|
|
366
|
+
"score": round(1.0 - reality_breaks / probe_count, 6),
|
|
367
|
+
"probes": len(external_probes),
|
|
368
|
+
"reality_breaks": reality_breaks,
|
|
369
|
+
},
|
|
370
|
+
"trace": [probe["ask"] for probe in internal_probes]
|
|
371
|
+
+ [probe["paraphrase"] for probe in internal_probes],
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _probe_divergence_step(
|
|
376
|
+
first: Sequence[Any],
|
|
377
|
+
second: Sequence[Any],
|
|
378
|
+
) -> Optional[int]:
|
|
379
|
+
"""First index where two probe trajectories fork (re-implementation of
|
|
380
|
+
the live/_stats.divergence_step SEMANTIC — never imported)."""
|
|
381
|
+
for index, (a, b) in enumerate(zip(first, second)):
|
|
382
|
+
if a != b:
|
|
383
|
+
return index
|
|
384
|
+
if len(first) != len(second):
|
|
385
|
+
return min(len(first), len(second))
|
|
386
|
+
return None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def calibrate_persona(
|
|
390
|
+
persona: Union[Persona, Mapping[str, Any]],
|
|
391
|
+
*,
|
|
392
|
+
library: Optional[Any] = None,
|
|
393
|
+
target_class: str = "schema_sampled",
|
|
394
|
+
repeats: int = 2,
|
|
395
|
+
seed: int = 7,
|
|
396
|
+
scenario: Optional[Scenario] = None,
|
|
397
|
+
bias_lint_result: Optional[Mapping[str, Any]] = None,
|
|
398
|
+
) -> Dict[str, Any]:
|
|
399
|
+
"""sampled -> validated -> interrogated -> admitted (R§3.1 lifecycle).
|
|
400
|
+
|
|
401
|
+
Deterministic, key-free, network-free. -> agent-learning.persona-calibration.v1.
|
|
402
|
+
Monotone upgrades only; cloud_downloaded/trace_mined are provenance facts,
|
|
403
|
+
never calibration outcomes. Any probe red -> the persona stays at its
|
|
404
|
+
current class; the artifact records which probe failed. Uncalibrated
|
|
405
|
+
personas RUN fine — they just carry the lowest class and cannot back
|
|
406
|
+
release claims (PRD §4.2)."""
|
|
407
|
+
if target_class not in _CALIBRATION_TARGETS:
|
|
408
|
+
raise ValueError(
|
|
409
|
+
f"target_class must be one of {list(_CALIBRATION_TARGETS)} "
|
|
410
|
+
"(cloud_downloaded and trace_mined are provenance facts, never "
|
|
411
|
+
"calibration outcomes)"
|
|
412
|
+
)
|
|
413
|
+
persona = upgrade_legacy_persona(persona)
|
|
414
|
+
stages: List[str] = ["sampled"]
|
|
415
|
+
|
|
416
|
+
validation = validate_persona(persona)
|
|
417
|
+
artifact: Dict[str, Any] = {
|
|
418
|
+
"kind": PERSONA_CALIBRATION_KIND,
|
|
419
|
+
"persona": {
|
|
420
|
+
"name": (persona.identity.name if persona.identity else None)
|
|
421
|
+
or persona.persona.get("name"),
|
|
422
|
+
"version_before": persona.version,
|
|
423
|
+
},
|
|
424
|
+
"validation": validation,
|
|
425
|
+
"representativeness_claim": "none",
|
|
426
|
+
}
|
|
427
|
+
if validation["status"] != "valid":
|
|
428
|
+
artifact.update({
|
|
429
|
+
"stages": stages,
|
|
430
|
+
"status": "failed",
|
|
431
|
+
"verdict": "not_admit_eligible",
|
|
432
|
+
"failed_probe": None,
|
|
433
|
+
"bias_lint": dict(bias_lint_result) if bias_lint_result else None,
|
|
434
|
+
})
|
|
435
|
+
return artifact
|
|
436
|
+
stages.append("validated")
|
|
437
|
+
|
|
438
|
+
runs = [
|
|
439
|
+
_battery_once(persona, scenario, seed=seed, repeat=index)
|
|
440
|
+
for index in range(max(1, int(repeats)))
|
|
441
|
+
]
|
|
442
|
+
base = runs[0]
|
|
443
|
+
divergence: Optional[int] = None
|
|
444
|
+
for repeat in runs[1:]:
|
|
445
|
+
step = _probe_divergence_step(base["trace"], repeat["trace"])
|
|
446
|
+
if step is not None:
|
|
447
|
+
divergence = step if divergence is None else min(divergence, step)
|
|
448
|
+
retest = {
|
|
449
|
+
"score": 1.0 if divergence is None else 0.0,
|
|
450
|
+
"replays": len(runs),
|
|
451
|
+
"divergence_step": divergence,
|
|
452
|
+
"method": "deterministic_replay",
|
|
453
|
+
}
|
|
454
|
+
probes = {
|
|
455
|
+
"internal": base["internal"],
|
|
456
|
+
"external": base["external"],
|
|
457
|
+
"retest": retest,
|
|
458
|
+
}
|
|
459
|
+
stages.append("interrogated")
|
|
460
|
+
|
|
461
|
+
failed_probe: Optional[str] = None
|
|
462
|
+
for leg in PERSONA_CALIBRATION_PROBES:
|
|
463
|
+
if probes[leg]["score"] < 1.0:
|
|
464
|
+
failed_probe = leg
|
|
465
|
+
break
|
|
466
|
+
|
|
467
|
+
current_class = (
|
|
468
|
+
persona.provenance.evidence_class if persona.provenance is not None else "legacy"
|
|
469
|
+
)
|
|
470
|
+
evidence = {"before": current_class, "after": current_class}
|
|
471
|
+
if failed_probe is None:
|
|
472
|
+
stages.append("admitted")
|
|
473
|
+
if current_class in _CALIBRATABLE_RANK and (
|
|
474
|
+
_CALIBRATABLE_RANK[current_class] < _CALIBRATABLE_RANK[target_class]
|
|
475
|
+
):
|
|
476
|
+
evidence["after"] = target_class
|
|
477
|
+
# else: monotone — keep the (equal-or-higher / provenance-fact) class
|
|
478
|
+
|
|
479
|
+
artifact.update({
|
|
480
|
+
"stages": stages,
|
|
481
|
+
"status": "passed" if failed_probe is None else "failed",
|
|
482
|
+
"verdict": "admit_eligible" if failed_probe is None else "not_admit_eligible",
|
|
483
|
+
"failed_probe": failed_probe,
|
|
484
|
+
"probes": probes,
|
|
485
|
+
"seed": seed,
|
|
486
|
+
"evidence_class": evidence,
|
|
487
|
+
"bias_lint": dict(bias_lint_result) if bias_lint_result else None,
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
if failed_probe is None:
|
|
491
|
+
ref_payload = json.dumps(
|
|
492
|
+
{k: v for k, v in artifact.items() if k != "persona_payload"},
|
|
493
|
+
sort_keys=True, separators=(",", ":"), default=str,
|
|
494
|
+
)
|
|
495
|
+
calibration_ref = "sha256:" + hashlib.sha256(
|
|
496
|
+
ref_payload.encode("utf-8")
|
|
497
|
+
).hexdigest()
|
|
498
|
+
provenance = (persona.provenance or PersonaProvenance()).model_copy(update={
|
|
499
|
+
"calibrated": True,
|
|
500
|
+
"calibration_ref": calibration_ref,
|
|
501
|
+
"evidence_class": evidence["after"],
|
|
502
|
+
})
|
|
503
|
+
persona = _rebuild(persona, provenance=provenance.model_dump(exclude_none=True))
|
|
504
|
+
artifact["calibration_ref"] = calibration_ref
|
|
505
|
+
artifact["persona"]["version_after"] = persona.version
|
|
506
|
+
artifact["persona_payload"] = persona.model_dump(exclude_none=True)
|
|
507
|
+
|
|
508
|
+
if library is not None:
|
|
509
|
+
hex_digest = persona.content_hash().split(":", 1)[1]
|
|
510
|
+
path = save_calibration(artifact, hex_digest, library=library)
|
|
511
|
+
artifact["artifact_path"] = str(path)
|
|
512
|
+
return artifact
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
__all__ = [
|
|
516
|
+
"PERSONA_CALIBRATION_PROBES",
|
|
517
|
+
"PERSONA_CALIBRATION_STAGES",
|
|
518
|
+
"build_persona",
|
|
519
|
+
"calibrate_persona",
|
|
520
|
+
"validate_persona",
|
|
521
|
+
"validate_scenario",
|
|
522
|
+
]
|