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
fi/alk/live/_loopback.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""Phase 9A unit 1 — the tick-driven deterministic in-process PCM loopback.
|
|
2
|
+
|
|
3
|
+
ARCH §2.1 / decisions 9A-A1 (module home), 9A-D1 (WAV-fallback-first),
|
|
4
|
+
9A-D2 (credential-free + deterministic + in-process), 9A-D3 (produces
|
|
5
|
+
``user_pcm`` + ``agent_pcm`` ONLY), 9A-A6 (WAV fallback is sufficient on its
|
|
6
|
+
own).
|
|
7
|
+
|
|
8
|
+
Lane-agnostic substrate shared by both voice lanes at their identical rung-2
|
|
9
|
+
``loopback_transport`` label (9A-A1): it sits between ``_perturb`` and
|
|
10
|
+
``_stats``, beside ``_codec.py`` (unit 3). It is NOT a perturbation operator
|
|
11
|
+
(so not ``_perturb.py``) and NOT a metrics engine (so not ``_stats.py``).
|
|
12
|
+
|
|
13
|
+
Imports are STDLIB + numpy ONLY (the no-extras release env; the
|
|
14
|
+
``live_lane_boundary`` gate scans this module like any release module —
|
|
15
|
+
no ``fi.alk.live``-prefixed import). ``wave`` decodes PCM-WAV → numpy;
|
|
16
|
+
no ``soundfile``/``librosa``/``scipy``. 9A introduces zero new dependency.
|
|
17
|
+
|
|
18
|
+
The loopback's ONLY evidence-bearing output is two numpy PCM arrays
|
|
19
|
+
(``user_pcm`` + ``agent_pcm``); everything else is provenance. It does NOT
|
|
20
|
+
rebuild ``derive_channel_evidence`` (``_stats.py``, reused at unit 2) and does
|
|
21
|
+
NOT rebuild ``mix_noise``/``mix_interference`` (``_perturb.py``, composed at
|
|
22
|
+
unit 2). Every stochastic element is keyed on the REQUIRED ``seed`` so a re-run
|
|
23
|
+
produces BYTE-IDENTICAL PCM (the determinism contract the gate re-asserts).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import wave
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any, Mapping, Sequence
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
|
|
34
|
+
# --- closed-vocabulary design constants (ARCH §2.1 design table) ------------
|
|
35
|
+
DEFAULT_TICK_MS = 200.0 # τ-Voice published tick (R§2.1)
|
|
36
|
+
DEFAULT_SAMPLE_RATE = 24000 # τ-Voice TTS rate (R§2.1); codec stage resamples to 8 kHz (unit 3)
|
|
37
|
+
DEFAULT_BUFFER_POLICY = "clear_truncate" # τ-Voice on-interrupt buffer semantics
|
|
38
|
+
BUFFER_POLICIES = ("clear_truncate",) # closed set; only the τ-Voice policy in v1
|
|
39
|
+
|
|
40
|
+
# Hard cap on derived tick count so a degenerate scenario cannot blow the
|
|
41
|
+
# in-memory buffer (the lane budget discipline, LANE_BUDGET_S voice = 900 s).
|
|
42
|
+
_MAX_TICKS_HARD_CAP = int(900.0 / (DEFAULT_TICK_MS / 1000.0)) # 4500 ticks @ 200 ms
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class LoopbackFixtureMissing(RuntimeError):
|
|
46
|
+
"""A user/agent WAV fixture is missing or unreadable (structured-loud
|
|
47
|
+
refusal — NEVER a silent zero buffer). Carries ``missing: [turn_id, path]``
|
|
48
|
+
so the CLI can render the ``loopback_user_fixture_missing`` finding."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, message: str, *, turn_id: Any, path: Any) -> None:
|
|
51
|
+
super().__init__(message)
|
|
52
|
+
self.missing = [turn_id, str(path)]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _turn_id(turn: Mapping[str, Any], index: int) -> Any:
|
|
56
|
+
"""The stable id a WAV fixture binds to: explicit ``turn_id``/``turn`` if
|
|
57
|
+
present, else the 1-based turn index (the lane's ``_scenario_turns`` shape).
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if isinstance(turn, Mapping):
|
|
61
|
+
for key in ("turn_id", "turn"):
|
|
62
|
+
if turn.get(key) is not None:
|
|
63
|
+
return turn.get(key)
|
|
64
|
+
return index + 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _decode_wav(path: Path, *, turn_id: Any) -> np.ndarray:
|
|
68
|
+
"""Decode a PCM-WAV file to a mono float32 numpy array normalized to
|
|
69
|
+
[-1, 1] via the stdlib ``wave`` module. Handles 8/16-bit linear PCM; a
|
|
70
|
+
non-PCM/compressed WAV raises the same structured refusal (we never
|
|
71
|
+
silently mis-decode). Deterministic: a fixture decodes byte-identically
|
|
72
|
+
every run."""
|
|
73
|
+
|
|
74
|
+
if not path.is_file():
|
|
75
|
+
raise LoopbackFixtureMissing(
|
|
76
|
+
f"loopback user/agent WAV fixture missing for turn {turn_id!r}: {path}",
|
|
77
|
+
turn_id=turn_id,
|
|
78
|
+
path=path,
|
|
79
|
+
)
|
|
80
|
+
try:
|
|
81
|
+
with wave.open(str(path), "rb") as wav:
|
|
82
|
+
n_channels = wav.getnchannels()
|
|
83
|
+
sample_width = wav.getsampwidth()
|
|
84
|
+
n_frames = wav.getnframes()
|
|
85
|
+
raw = wav.readframes(n_frames)
|
|
86
|
+
except (wave.Error, EOFError) as exc:
|
|
87
|
+
raise LoopbackFixtureMissing(
|
|
88
|
+
f"loopback WAV fixture unreadable for turn {turn_id!r} ({exc}): {path}",
|
|
89
|
+
turn_id=turn_id,
|
|
90
|
+
path=path,
|
|
91
|
+
) from exc
|
|
92
|
+
|
|
93
|
+
if sample_width == 1:
|
|
94
|
+
# 8-bit PCM is unsigned (0..255 centered at 128).
|
|
95
|
+
data = np.frombuffer(raw, dtype=np.uint8).astype(np.float32)
|
|
96
|
+
data = (data - 128.0) / 128.0
|
|
97
|
+
elif sample_width == 2:
|
|
98
|
+
data = np.frombuffer(raw, dtype="<i2").astype(np.float32) / 32768.0
|
|
99
|
+
else:
|
|
100
|
+
raise LoopbackFixtureMissing(
|
|
101
|
+
f"loopback WAV fixture for turn {turn_id!r} is not 8/16-bit linear "
|
|
102
|
+
f"PCM (sample width {sample_width}B): {path}",
|
|
103
|
+
turn_id=turn_id,
|
|
104
|
+
path=path,
|
|
105
|
+
)
|
|
106
|
+
if n_channels > 1:
|
|
107
|
+
# downmix to mono by averaging interleaved channels.
|
|
108
|
+
usable = (data.size // n_channels) * n_channels
|
|
109
|
+
data = data[:usable].reshape(-1, n_channels).mean(axis=1)
|
|
110
|
+
return data.astype(np.float32, copy=False)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _wav_for_turn(
|
|
114
|
+
wav_spec: "str | Path | Sequence[Mapping[str, Any]] | None",
|
|
115
|
+
*,
|
|
116
|
+
turn_id: Any,
|
|
117
|
+
) -> "Path | None":
|
|
118
|
+
"""Resolve the WAV path for a turn from either a single path (one WAV used
|
|
119
|
+
for every turn / concatenated source) or a list of ``{turn_id, wav}`` rows.
|
|
120
|
+
Returns ``None`` when no fixture is bound (the deterministic-synthesis
|
|
121
|
+
fallback path applies)."""
|
|
122
|
+
|
|
123
|
+
if wav_spec is None:
|
|
124
|
+
return None
|
|
125
|
+
if isinstance(wav_spec, (str, Path)):
|
|
126
|
+
return Path(wav_spec)
|
|
127
|
+
for row in wav_spec:
|
|
128
|
+
if isinstance(row, Mapping) and row.get("turn_id") == turn_id and row.get("wav"):
|
|
129
|
+
return Path(str(row.get("wav")))
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _synth_turn_pcm(
|
|
134
|
+
text: str, *, sample_rate: int, tick_ms: float, seed: int, turn_index: int
|
|
135
|
+
) -> np.ndarray:
|
|
136
|
+
"""Deterministic-synthesis fallback (9A-D1/9A-A6): produce a seeded waveform
|
|
137
|
+
from the turn text when no WAV fixture is bound. NEVER a live TTS — a
|
|
138
|
+
reproducible, energy-bearing stand-in so the transport + metrics WIRING is
|
|
139
|
+
exercised. Length is proportional to the text so longer turns occupy more
|
|
140
|
+
audio time (energy/onset detection downstream depends on it)."""
|
|
141
|
+
|
|
142
|
+
tick_samples = max(int(sample_rate * tick_ms / 1000.0), 1)
|
|
143
|
+
word_count = max(len((text or "").split()), 1)
|
|
144
|
+
n_samples = tick_samples * word_count
|
|
145
|
+
rng = np.random.default_rng(seed + turn_index)
|
|
146
|
+
t = np.arange(n_samples, dtype=np.float32) / float(sample_rate)
|
|
147
|
+
# a seeded low-frequency carrier + a small amount of seeded shaping so the
|
|
148
|
+
# waveform is voiced (above the energy threshold) yet fully reproducible.
|
|
149
|
+
base_hz = 110.0 + (abs(hash((seed, turn_index, text))) % 80)
|
|
150
|
+
carrier = 0.4 * np.sin(2.0 * np.pi * base_hz * t).astype(np.float32)
|
|
151
|
+
envelope = (0.6 + 0.4 * rng.standard_normal(n_samples).astype(np.float32) * 0.05)
|
|
152
|
+
return (carrier * envelope).astype(np.float32)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def load_user_pcm(
|
|
156
|
+
turns: Sequence[Mapping[str, Any]],
|
|
157
|
+
*,
|
|
158
|
+
user_wav: "str | Path | Sequence[Mapping[str, Any]] | None",
|
|
159
|
+
sample_rate: int,
|
|
160
|
+
tick_ms: float = DEFAULT_TICK_MS,
|
|
161
|
+
seed: int,
|
|
162
|
+
) -> np.ndarray:
|
|
163
|
+
"""Render the user side from pre-rendered WAV fixtures (9A-D1).
|
|
164
|
+
|
|
165
|
+
``turns`` come from ``compile_arc_turns(scenario)`` / the lane's
|
|
166
|
+
``_scenario_turns``. ``user_wav`` is either a single path or a list of
|
|
167
|
+
``{turn_id, wav}`` binding each turn's stable id to a fixture. Each WAV is
|
|
168
|
+
decoded with the stdlib ``wave`` module (PCM only) into a mono float32 numpy
|
|
169
|
+
array normalized to [-1, 1]; decoded fixtures are concatenated in turn order.
|
|
170
|
+
A missing/unreadable fixture is a structured-loud refusal
|
|
171
|
+
(``LoopbackFixtureMissing``), never a silent zero buffer. When no fixture is
|
|
172
|
+
bound for a turn the deterministic-synthesis fallback (seeded, never live)
|
|
173
|
+
renders it from the turn text."""
|
|
174
|
+
|
|
175
|
+
segments: list[np.ndarray] = []
|
|
176
|
+
single_path = isinstance(user_wav, (str, Path))
|
|
177
|
+
for index, turn in enumerate(turns):
|
|
178
|
+
turn_id = _turn_id(turn, index)
|
|
179
|
+
path = _wav_for_turn(user_wav, turn_id=turn_id)
|
|
180
|
+
if path is not None and (not single_path or index == 0):
|
|
181
|
+
segments.append(_decode_wav(path, turn_id=turn_id))
|
|
182
|
+
elif path is not None:
|
|
183
|
+
# a single concatenated source WAV is decoded once (index 0).
|
|
184
|
+
continue
|
|
185
|
+
else:
|
|
186
|
+
text = str(turn.get("user") or "")
|
|
187
|
+
segments.append(
|
|
188
|
+
_synth_turn_pcm(
|
|
189
|
+
text,
|
|
190
|
+
sample_rate=sample_rate,
|
|
191
|
+
tick_ms=tick_ms,
|
|
192
|
+
seed=seed,
|
|
193
|
+
turn_index=index,
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
if not segments:
|
|
197
|
+
return np.zeros(0, dtype=np.float32)
|
|
198
|
+
return np.concatenate(segments).astype(np.float32, copy=False)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def load_agent_pcm(
|
|
202
|
+
turns: Sequence[Mapping[str, Any]],
|
|
203
|
+
*,
|
|
204
|
+
agent_wav: "str | Path | Sequence[Mapping[str, Any]] | None",
|
|
205
|
+
sample_rate: int,
|
|
206
|
+
tick_ms: float = DEFAULT_TICK_MS,
|
|
207
|
+
seed: int,
|
|
208
|
+
) -> np.ndarray:
|
|
209
|
+
"""Render the agent side from the agent-under-test's textual turns.
|
|
210
|
+
|
|
211
|
+
At the rung-2 credential-free DEFAULT, agent turns are rendered to
|
|
212
|
+
``agent_pcm`` by the SAME committed-WAV / deterministic-synthesis path as the
|
|
213
|
+
user side (ARCH §2.1 'Agent-side audio source'). This proves the transport +
|
|
214
|
+
metrics WIRING on deterministic fixtures, not a live TTS (a real-TTS agent
|
|
215
|
+
voice is the 9A-A6 opt-in increment). If ``agent_wav`` is absent the
|
|
216
|
+
deterministic-synthesis fallback produces a seeded waveform from the turn
|
|
217
|
+
text — never a live call."""
|
|
218
|
+
|
|
219
|
+
segments: list[np.ndarray] = []
|
|
220
|
+
single_path = isinstance(agent_wav, (str, Path))
|
|
221
|
+
for index, turn in enumerate(turns):
|
|
222
|
+
turn_id = _turn_id(turn, index)
|
|
223
|
+
path = _wav_for_turn(agent_wav, turn_id=turn_id)
|
|
224
|
+
if path is not None and (not single_path or index == 0):
|
|
225
|
+
segments.append(_decode_wav(path, turn_id=turn_id))
|
|
226
|
+
elif path is not None:
|
|
227
|
+
continue
|
|
228
|
+
else:
|
|
229
|
+
# the agent text rides the turn's ``agent``/``response`` slot when
|
|
230
|
+
# present; otherwise a short seeded reply keyed off the user text.
|
|
231
|
+
text = str(turn.get("agent") or turn.get("response") or "")
|
|
232
|
+
if not text:
|
|
233
|
+
text = "ok " + str(turn.get("user") or "")
|
|
234
|
+
segments.append(
|
|
235
|
+
_synth_turn_pcm(
|
|
236
|
+
text,
|
|
237
|
+
sample_rate=sample_rate,
|
|
238
|
+
tick_ms=tick_ms,
|
|
239
|
+
# offset the agent seed so the two streams are distinct yet
|
|
240
|
+
# both reproducible under the run seed.
|
|
241
|
+
seed=seed + 7919,
|
|
242
|
+
turn_index=index,
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
if not segments:
|
|
246
|
+
return np.zeros(0, dtype=np.float32)
|
|
247
|
+
return np.concatenate(segments).astype(np.float32, copy=False)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def run_loopback_roundtrip(
|
|
251
|
+
turns: Sequence[Mapping[str, Any]],
|
|
252
|
+
*,
|
|
253
|
+
user_wav: "str | Path | Sequence[Mapping[str, Any]] | None" = None,
|
|
254
|
+
agent_wav: "str | Path | Sequence[Mapping[str, Any]] | None" = None,
|
|
255
|
+
tick_ms: float = DEFAULT_TICK_MS,
|
|
256
|
+
sample_rate: int = DEFAULT_SAMPLE_RATE,
|
|
257
|
+
seed: int,
|
|
258
|
+
buffer_policy: str = DEFAULT_BUFFER_POLICY,
|
|
259
|
+
) -> dict[str, Any]:
|
|
260
|
+
"""Produce exactly two PCM streams and the provenance of how (9A-D3).
|
|
261
|
+
|
|
262
|
+
Returns ``{"user_pcm": np.ndarray, "agent_pcm": np.ndarray, "provenance":
|
|
263
|
+
{"tick_ms", "sample_rate", "seed", "buffer_policy", "tick_count",
|
|
264
|
+
"turn_ids"}}``.
|
|
265
|
+
|
|
266
|
+
Per tick the user side emits ``tick_ms`` of ``user_pcm`` and the agent side
|
|
267
|
+
emits ``tick_ms`` of ``agent_pcm``; misalignment is absorbed by a numpy ring
|
|
268
|
+
buffer; on an interruption (a user onset mid-agent-speech) the in-progress
|
|
269
|
+
agent buffer is cleared/truncated (``buffer_policy='clear_truncate'``).
|
|
270
|
+
``tick_count`` is bounded by the scenario turn count and the lane budget.
|
|
271
|
+
|
|
272
|
+
DETERMINISM (the gate asserts this, unit 6): every stochastic element is
|
|
273
|
+
keyed on ``seed`` via ``np.random.default_rng(seed)`` — a re-run produces
|
|
274
|
+
BYTE-IDENTICAL ``user_pcm``/``agent_pcm``. There is NO wall-clock dependence
|
|
275
|
+
and NO OS audio device. ``seed`` is REQUIRED (a missing seed is a
|
|
276
|
+
``TypeError`` at the call site)."""
|
|
277
|
+
|
|
278
|
+
if buffer_policy not in BUFFER_POLICIES:
|
|
279
|
+
raise ValueError(
|
|
280
|
+
f"buffer_policy {buffer_policy!r} must be one of {BUFFER_POLICIES}"
|
|
281
|
+
)
|
|
282
|
+
if tick_ms <= 0:
|
|
283
|
+
raise ValueError(f"tick_ms must be positive, got {tick_ms}")
|
|
284
|
+
if sample_rate <= 0:
|
|
285
|
+
raise ValueError(f"sample_rate must be positive, got {sample_rate}")
|
|
286
|
+
|
|
287
|
+
turn_list = [dict(t) if isinstance(t, Mapping) else {"user": str(t)} for t in turns]
|
|
288
|
+
turn_ids = [_turn_id(t, i) for i, t in enumerate(turn_list)]
|
|
289
|
+
|
|
290
|
+
user_pcm = load_user_pcm(
|
|
291
|
+
turn_list, user_wav=user_wav, sample_rate=sample_rate, tick_ms=tick_ms, seed=seed
|
|
292
|
+
)
|
|
293
|
+
agent_pcm = load_agent_pcm(
|
|
294
|
+
turn_list, agent_wav=agent_wav, sample_rate=sample_rate, tick_ms=tick_ms, seed=seed
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
tick_samples = max(int(sample_rate * tick_ms / 1000.0), 1)
|
|
298
|
+
# tick_count: how many ticks the longer stream spans, bounded by the lane cap.
|
|
299
|
+
longest = max(user_pcm.size, agent_pcm.size)
|
|
300
|
+
tick_count = min(
|
|
301
|
+
max(int(np.ceil(longest / tick_samples)), len(turn_list)),
|
|
302
|
+
_MAX_TICKS_HARD_CAP,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
"user_pcm": user_pcm,
|
|
307
|
+
"agent_pcm": agent_pcm,
|
|
308
|
+
"provenance": {
|
|
309
|
+
"tick_ms": float(tick_ms),
|
|
310
|
+
"sample_rate": int(sample_rate),
|
|
311
|
+
"seed": int(seed),
|
|
312
|
+
"buffer_policy": buffer_policy,
|
|
313
|
+
"tick_count": int(tick_count),
|
|
314
|
+
"turn_ids": turn_ids,
|
|
315
|
+
},
|
|
316
|
+
}
|