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,138 @@
|
|
|
1
|
+
"""LiveKit target-agent adapter.
|
|
2
|
+
|
|
3
|
+
Thin adapter around the existing ``LiveKitEngine`` that lets hosted +
|
|
4
|
+
matrix runners see a LiveKit target through the ``AgentEndpoint``
|
|
5
|
+
contract. The actual media/lifecycle work still lives inside the
|
|
6
|
+
engine's per-case runner; this class exposes a stable seam so a full
|
|
7
|
+
``endpoints/livekit.py`` rewrite can land later without breaking
|
|
8
|
+
callers.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import uuid
|
|
14
|
+
from collections.abc import AsyncIterator
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
|
|
17
|
+
from fi.simulate.agent.definition import AgentDefinition, TelephonyTransport
|
|
18
|
+
from fi.simulate.realtime.events import RealtimeEvent
|
|
19
|
+
from fi.simulate.realtime.media import AudioFrame
|
|
20
|
+
from fi.simulate.runtime.capabilities import EndpointCapabilities
|
|
21
|
+
|
|
22
|
+
from .base import (
|
|
23
|
+
AgentEndpointManifest,
|
|
24
|
+
DiscoveryRequest,
|
|
25
|
+
DiscoverySnapshot,
|
|
26
|
+
EndpointHandle,
|
|
27
|
+
ReadinessResult,
|
|
28
|
+
ReconciliationResult,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _capabilities_for(agent_definition: AgentDefinition) -> EndpointCapabilities:
|
|
33
|
+
transport = agent_definition.transport or TelephonyTransport()
|
|
34
|
+
kind = transport.kind
|
|
35
|
+
return EndpointCapabilities(
|
|
36
|
+
audio=True,
|
|
37
|
+
text=True,
|
|
38
|
+
streaming=True,
|
|
39
|
+
interruption=True,
|
|
40
|
+
dtmf=kind in {"sip_inbound", "sip_outbound"},
|
|
41
|
+
transfer=kind in {"sip_inbound", "sip_outbound"},
|
|
42
|
+
transcript_events=True,
|
|
43
|
+
tool_events=False,
|
|
44
|
+
usage_events=True,
|
|
45
|
+
internal_metrics=True,
|
|
46
|
+
recording=True,
|
|
47
|
+
web_rtc=kind == "webrtc",
|
|
48
|
+
sip=kind in {"sip_inbound", "sip_outbound"},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class LiveKitAgentEndpoint:
|
|
53
|
+
"""Spec-level LiveKit endpoint. Engine still owns the media path."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, agent_definition: AgentDefinition, *, name: str | None = None) -> None:
|
|
56
|
+
self._agent_definition = agent_definition
|
|
57
|
+
self.manifest = AgentEndpointManifest(
|
|
58
|
+
name=name or agent_definition.name,
|
|
59
|
+
provider="livekit",
|
|
60
|
+
world_kinds=["voice"],
|
|
61
|
+
capabilities=_capabilities_for(agent_definition),
|
|
62
|
+
metadata={
|
|
63
|
+
"url": str(agent_definition.url),
|
|
64
|
+
"room_name_template": agent_definition.room_name,
|
|
65
|
+
"room_mode": agent_definition.room_mode,
|
|
66
|
+
"transport": (
|
|
67
|
+
agent_definition.transport.kind
|
|
68
|
+
if agent_definition.transport
|
|
69
|
+
else "webrtc"
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
self.capabilities = self.manifest.capabilities
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def agent_definition(self) -> AgentDefinition:
|
|
77
|
+
return self._agent_definition
|
|
78
|
+
|
|
79
|
+
async def discover(self, request: DiscoveryRequest) -> DiscoverySnapshot:
|
|
80
|
+
supported = self.capabilities.supported()
|
|
81
|
+
missing = [
|
|
82
|
+
cap for cap in request.required_capabilities if cap not in supported
|
|
83
|
+
]
|
|
84
|
+
return DiscoverySnapshot(
|
|
85
|
+
capabilities=self.capabilities,
|
|
86
|
+
supported=not missing,
|
|
87
|
+
reasons=[f"unsupported:{cap}" for cap in missing],
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
async def prepare(self, plan) -> EndpointHandle: # noqa: ANN001
|
|
91
|
+
return EndpointHandle(
|
|
92
|
+
handle_id=f"lk-{uuid.uuid4().hex[:12]}",
|
|
93
|
+
endpoint_name=self.manifest.name,
|
|
94
|
+
created_at=datetime.now(timezone.utc),
|
|
95
|
+
metadata={
|
|
96
|
+
"plan_id": getattr(plan, "plan_id", None),
|
|
97
|
+
"transport": (
|
|
98
|
+
self._agent_definition.transport.kind
|
|
99
|
+
if self._agent_definition.transport
|
|
100
|
+
else "webrtc"
|
|
101
|
+
),
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
async def wait_ready(self, handle: EndpointHandle) -> ReadinessResult:
|
|
106
|
+
del handle
|
|
107
|
+
# Real readiness is driven inside LiveKitEngine per case today.
|
|
108
|
+
# This adapter reports ready=True optimistically; hosted callers
|
|
109
|
+
# observe actual readiness through emitted CanonicalEvents.
|
|
110
|
+
return ReadinessResult(ready=True)
|
|
111
|
+
|
|
112
|
+
async def send(
|
|
113
|
+
self, handle: EndpointHandle, event: RealtimeEvent | AudioFrame
|
|
114
|
+
) -> None:
|
|
115
|
+
raise NotImplementedError(
|
|
116
|
+
"LiveKitAgentEndpoint.send belongs to the future realtime router"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
async def receive(
|
|
120
|
+
self, handle: EndpointHandle
|
|
121
|
+
) -> AsyncIterator[RealtimeEvent | AudioFrame]:
|
|
122
|
+
raise NotImplementedError(
|
|
123
|
+
"LiveKitAgentEndpoint.receive belongs to the future realtime router"
|
|
124
|
+
)
|
|
125
|
+
yield # type: ignore[unreachable]
|
|
126
|
+
|
|
127
|
+
async def stop(self, handle: EndpointHandle) -> None:
|
|
128
|
+
del handle
|
|
129
|
+
|
|
130
|
+
async def cleanup(self, handle: EndpointHandle) -> None:
|
|
131
|
+
del handle
|
|
132
|
+
|
|
133
|
+
async def reconcile(self, handle: EndpointHandle) -> ReconciliationResult:
|
|
134
|
+
del handle
|
|
135
|
+
return ReconciliationResult(reconciled=True)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
__all__ = ["LiveKitAgentEndpoint"]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Provider-neutral call-origination surface — factory + cleanup helper.
|
|
2
|
+
|
|
3
|
+
Lives outside ``simulation/engines/livekit.py`` on purpose: that module
|
|
4
|
+
raises ``ImportError`` without the optional ``livekit`` extra (which this
|
|
5
|
+
SDK's dev venv lacks), so nothing inside it can be exercised by tests here;
|
|
6
|
+
this module has no such dependency, so it is fully testable (C3/C6).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from typing import Any, Callable, Protocol
|
|
15
|
+
|
|
16
|
+
from .retell import RetellCallOriginator
|
|
17
|
+
from .vapi import VapiCallOriginator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CallOriginator(Protocol):
|
|
21
|
+
"""The lifecycle every provider originator satisfies (C2/C3)."""
|
|
22
|
+
|
|
23
|
+
async def start(self) -> Any: ...
|
|
24
|
+
|
|
25
|
+
async def stop(self, call_id: str) -> None: ...
|
|
26
|
+
|
|
27
|
+
async def reconcile_and_stop(
|
|
28
|
+
self, *, started_after_ms: int, ended_before_ms: int
|
|
29
|
+
) -> list[str]: ...
|
|
30
|
+
|
|
31
|
+
async def close(self) -> None: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def build_call_originator(transport: Any) -> CallOriginator:
|
|
35
|
+
"""Construct the originator named by ``transport.inbound_call_originator``.
|
|
36
|
+
|
|
37
|
+
This is the one place a provider name is compared to select a class;
|
|
38
|
+
construction, start, stop and cleanup afterward stay provider-neutral.
|
|
39
|
+
"""
|
|
40
|
+
name = transport.inbound_call_originator
|
|
41
|
+
if name == "vapi":
|
|
42
|
+
return VapiCallOriginator.from_env()
|
|
43
|
+
if name == "retell":
|
|
44
|
+
return RetellCallOriginator.from_env(transport)
|
|
45
|
+
raise ValueError(f"unsupported_inbound_call_originator: {name}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class OriginatorFinalizeResult:
|
|
50
|
+
"""What cleanup did, so the engine can adapt it without deciding anything."""
|
|
51
|
+
|
|
52
|
+
termination_source: str | None
|
|
53
|
+
reconciled_call_ids: list[str]
|
|
54
|
+
cleanup_errors: list[tuple[str, Exception]]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def finalize_originator(
|
|
58
|
+
originator: CallOriginator,
|
|
59
|
+
*,
|
|
60
|
+
provider_call_id: str | None,
|
|
61
|
+
originator_name: str,
|
|
62
|
+
case_started_at: datetime,
|
|
63
|
+
cleanup_timeout: float,
|
|
64
|
+
now: Callable[[], datetime] = lambda: datetime.now(timezone.utc),
|
|
65
|
+
) -> OriginatorFinalizeResult:
|
|
66
|
+
"""Best-effort originator cleanup: never raises (C3).
|
|
67
|
+
|
|
68
|
+
``stop()`` ends the call we know about; with no call id in hand (a
|
|
69
|
+
cancelled ``asyncio.wait_for`` can still leave a live, billed call on
|
|
70
|
+
the provider's side) ``reconcile_and_stop`` hunts for the orphan instead.
|
|
71
|
+
``close()`` always runs, even when the above raised.
|
|
72
|
+
"""
|
|
73
|
+
termination_source: str | None = None
|
|
74
|
+
reconciled_call_ids: list[str] = []
|
|
75
|
+
cleanup_errors: list[tuple[str, Exception]] = []
|
|
76
|
+
try:
|
|
77
|
+
if provider_call_id is not None:
|
|
78
|
+
try:
|
|
79
|
+
await asyncio.wait_for(
|
|
80
|
+
originator.stop(provider_call_id), timeout=cleanup_timeout
|
|
81
|
+
)
|
|
82
|
+
termination_source = "sdk_originator_cleanup"
|
|
83
|
+
except Exception as exc: # noqa: BLE001 - best-effort cleanup, never raises
|
|
84
|
+
cleanup_errors.append((f"{originator_name}_call_stop", exc))
|
|
85
|
+
else:
|
|
86
|
+
# A naive datetime is read as local time by .timestamp(), silently
|
|
87
|
+
# widening the blast-radius window; treat it as UTC instead.
|
|
88
|
+
started = (
|
|
89
|
+
case_started_at
|
|
90
|
+
if case_started_at.tzinfo is not None
|
|
91
|
+
else case_started_at.replace(tzinfo=timezone.utc)
|
|
92
|
+
)
|
|
93
|
+
started_after_ms = int(started.timestamp() * 1000)
|
|
94
|
+
ended_before_ms = int(now().timestamp() * 1000)
|
|
95
|
+
try:
|
|
96
|
+
reconciled_call_ids = await asyncio.wait_for(
|
|
97
|
+
originator.reconcile_and_stop(
|
|
98
|
+
started_after_ms=started_after_ms,
|
|
99
|
+
ended_before_ms=ended_before_ms,
|
|
100
|
+
),
|
|
101
|
+
timeout=cleanup_timeout,
|
|
102
|
+
)
|
|
103
|
+
if reconciled_call_ids:
|
|
104
|
+
termination_source = "sdk_originator_cleanup"
|
|
105
|
+
except TypeError as exc:
|
|
106
|
+
# A wrong call site (non-int epoch ms) fails loudly and
|
|
107
|
+
# distinguishably from a swallowed HTTP failure.
|
|
108
|
+
cleanup_errors.append(
|
|
109
|
+
(f"{originator_name}_call_reconcile_bad_arguments", exc)
|
|
110
|
+
)
|
|
111
|
+
except Exception as exc: # noqa: BLE001 - best-effort cleanup, never raises
|
|
112
|
+
cleanup_errors.append((f"{originator_name}_call_reconcile", exc))
|
|
113
|
+
finally:
|
|
114
|
+
try:
|
|
115
|
+
# Bounded like every other cleanup step, so a slow client can't
|
|
116
|
+
# stall this case past its cleanup budget.
|
|
117
|
+
await asyncio.wait_for(originator.close(), cleanup_timeout)
|
|
118
|
+
except Exception as exc: # noqa: BLE001 - best-effort cleanup, never raises
|
|
119
|
+
cleanup_errors.append((f"{originator_name}_call_close", exc))
|
|
120
|
+
return OriginatorFinalizeResult(
|
|
121
|
+
termination_source=termination_source,
|
|
122
|
+
reconciled_call_ids=reconciled_call_ids,
|
|
123
|
+
cleanup_errors=cleanup_errors,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
"CallOriginator",
|
|
129
|
+
"OriginatorFinalizeResult",
|
|
130
|
+
"build_call_originator",
|
|
131
|
+
"finalize_originator",
|
|
132
|
+
]
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"""Target-endpoint profiles (canonical plan §4.1) — the factory that retires the
|
|
2
|
+
hardcoded ``transport.kind`` branches.
|
|
3
|
+
|
|
4
|
+
A profile is the single declarative record for one target adapter: its capability
|
|
5
|
+
manifest plus the *decisions* the voice engine used to make with ``if
|
|
6
|
+
transport.kind == ...`` chains — whether the leg is SIP, whether it places an
|
|
7
|
+
outbound call, whether it needs a web audio bridge, which provider evidence to
|
|
8
|
+
collect, and which connector to build. The engine keeps its session/audio
|
|
9
|
+
*mechanics*; it just asks the profile instead of branching on strings.
|
|
10
|
+
|
|
11
|
+
Adding a provider = registering one profile here (or via the
|
|
12
|
+
``fi.simulate.endpoints`` entry-point group) with **zero engine edits**.
|
|
13
|
+
|
|
14
|
+
Import weight: connector / ``livekit.rtc`` imports are deferred into
|
|
15
|
+
``build_connector`` so the planner can read ``profile.manifest.capabilities``
|
|
16
|
+
without pulling the optional voice stack.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import Any, Callable, Optional
|
|
22
|
+
|
|
23
|
+
from fi.simulate.endpoints.actor_sources import (
|
|
24
|
+
resolve_factory,
|
|
25
|
+
resolve_framework,
|
|
26
|
+
resolve_http,
|
|
27
|
+
resolve_import_object,
|
|
28
|
+
resolve_python_callable,
|
|
29
|
+
resolve_system_prompt,
|
|
30
|
+
)
|
|
31
|
+
from fi.simulate.endpoints.base import AgentEndpointManifest
|
|
32
|
+
from fi.simulate.registry import register_endpoint
|
|
33
|
+
from fi.simulate.runtime.capabilities import EndpointCapabilities
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EndpointProfile:
|
|
37
|
+
"""One target adapter's manifest + provider decisions."""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
manifest: AgentEndpointManifest,
|
|
42
|
+
*,
|
|
43
|
+
is_sip: bool = False,
|
|
44
|
+
places_outbound_call: bool = False,
|
|
45
|
+
receives_inbound_call: bool = False,
|
|
46
|
+
uses_web_audio_bridge: bool = False,
|
|
47
|
+
bridge_provider: Optional[str] = None,
|
|
48
|
+
evidence_provider: Optional[str] = None,
|
|
49
|
+
required_env_rule: Optional[Callable[[Any], list[str]]] = None,
|
|
50
|
+
connector_builder: Optional[Callable[..., Any]] = None,
|
|
51
|
+
target_resolver: Optional[Callable[..., Any]] = None,
|
|
52
|
+
runs_caller_code: bool = True,
|
|
53
|
+
) -> None:
|
|
54
|
+
self.manifest = manifest
|
|
55
|
+
self.is_sip = is_sip
|
|
56
|
+
self.places_outbound_call = places_outbound_call
|
|
57
|
+
self.receives_inbound_call = receives_inbound_call
|
|
58
|
+
self.uses_web_audio_bridge = uses_web_audio_bridge
|
|
59
|
+
self.bridge_provider = bridge_provider
|
|
60
|
+
self.evidence_provider = evidence_provider
|
|
61
|
+
self._required_env_rule = required_env_rule
|
|
62
|
+
self._connector_builder = connector_builder
|
|
63
|
+
self._target_resolver = target_resolver
|
|
64
|
+
# Deny-by-default: a turn-based target executes caller-supplied Python
|
|
65
|
+
# unless it explicitly declares otherwise (http/system_prompt/deployed
|
|
66
|
+
# endpoints). An undeclared third-party kind is treated as unsafe. The
|
|
67
|
+
# hosted gate (resolve_chat_target) reads this, not a module denylist.
|
|
68
|
+
self.runs_caller_code = runs_caller_code
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def is_turn_based_target(self) -> bool:
|
|
72
|
+
"""This adapter resolves to a turn-based agent (chat) rather than a
|
|
73
|
+
realtime/voice endpoint."""
|
|
74
|
+
return self._target_resolver is not None
|
|
75
|
+
|
|
76
|
+
def resolve_target(
|
|
77
|
+
self, config: Any = None, secret_refs: Any = None, *, hosted: bool = False
|
|
78
|
+
) -> Any:
|
|
79
|
+
"""Resolve this actor-source to a runnable turn-based agent (plan §4.1).
|
|
80
|
+
|
|
81
|
+
Raises for realtime/voice adapters, which are reached through
|
|
82
|
+
``build_connector`` + the engine instead. ``hosted`` restricts env reads
|
|
83
|
+
to job-provisioned secrets (see resolvers)."""
|
|
84
|
+
if self._target_resolver is None:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"target_adapter_not_turn_based: {self.manifest.name!r}"
|
|
87
|
+
)
|
|
88
|
+
return self._target_resolver(config or {}, secret_refs, hosted=hosted)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def joins_as_sip_participant(self) -> bool:
|
|
92
|
+
"""The target joins the room as a SIP participant — true for real SIP
|
|
93
|
+
legs and for the web providers reached through the LiveKit audio bridge."""
|
|
94
|
+
return self.is_sip or self.uses_web_audio_bridge
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def uses_external_room(self) -> bool:
|
|
98
|
+
"""Only plain WebRTC targets run in an external (non-managed) room."""
|
|
99
|
+
return not self.joins_as_sip_participant
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def name(self) -> str:
|
|
103
|
+
return self.manifest.name
|
|
104
|
+
|
|
105
|
+
def required_env(self, agent_definition: Any) -> list[str]:
|
|
106
|
+
"""Transport-specific env var *names* this adapter needs (the branch that
|
|
107
|
+
used to live in ``voice._voice_required_env``). Base adapters need none."""
|
|
108
|
+
if self._required_env_rule is None:
|
|
109
|
+
return []
|
|
110
|
+
return list(self._required_env_rule(agent_definition))
|
|
111
|
+
|
|
112
|
+
def build_connector(
|
|
113
|
+
self, provider_target: Any, *, conversation_direction: str
|
|
114
|
+
) -> Any:
|
|
115
|
+
"""Provider connector for web-bridged targets; ``None`` for webrtc/sip."""
|
|
116
|
+
if self._connector_builder is None:
|
|
117
|
+
return None
|
|
118
|
+
return self._connector_builder(
|
|
119
|
+
provider_target, conversation_direction=conversation_direction
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# --------------------------------------------------------------------------- #
|
|
124
|
+
# required-env rules (parity with the old voice._voice_required_env branches)
|
|
125
|
+
# --------------------------------------------------------------------------- #
|
|
126
|
+
def _vapi_web_required_env(agent_definition: Any) -> list[str]:
|
|
127
|
+
if agent_definition.target is None:
|
|
128
|
+
return ["VAPI_API_KEY", "VAPI_ASSISTANT_ID"]
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _retell_web_required_env(agent_definition: Any) -> list[str]:
|
|
133
|
+
if agent_definition.target is None:
|
|
134
|
+
return ["RETELL_API_KEY", "RETELL_AGENT_ID"]
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _sip_inbound_required_env(agent_definition: Any) -> list[str]:
|
|
139
|
+
transport = agent_definition.transport
|
|
140
|
+
target = agent_definition.target
|
|
141
|
+
names: list[str] = []
|
|
142
|
+
if transport is not None and not transport.dispatch_rule_name:
|
|
143
|
+
names.append("LIVEKIT_INBOUND_TRUNK_ID")
|
|
144
|
+
if transport is not None and transport.inbound_call_originator == "vapi":
|
|
145
|
+
# Same conditional-append idiom as the Retell branch below; behaviour
|
|
146
|
+
# identical (the consumer already dropped blanks from the old tuple).
|
|
147
|
+
names.append(
|
|
148
|
+
target.api_key_env
|
|
149
|
+
if target is not None and target.provider == "vapi"
|
|
150
|
+
else "VAPI_API_KEY"
|
|
151
|
+
)
|
|
152
|
+
if target is None or target.provider != "vapi":
|
|
153
|
+
names.append("VAPI_ASSISTANT_ID")
|
|
154
|
+
names.append("VAPI_PHONE_NUMBER_ID")
|
|
155
|
+
names.append("LIVEKIT_INBOUND_DID")
|
|
156
|
+
if transport is not None and transport.inbound_call_originator == "retell":
|
|
157
|
+
# Parity-only rule (CLI/manifest path); the job carries these fields directly.
|
|
158
|
+
names.append("RETELL_API_KEY")
|
|
159
|
+
if not transport.originator_agent_id:
|
|
160
|
+
names.append("RETELL_AGENT_ID")
|
|
161
|
+
if not transport.originator_from_number:
|
|
162
|
+
names.append("RETELL_FROM_NUMBER")
|
|
163
|
+
names.append("LIVEKIT_INBOUND_DID")
|
|
164
|
+
return names
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# --------------------------------------------------------------------------- #
|
|
168
|
+
# connector builders (lazy imports keep the voice stack off the planner path)
|
|
169
|
+
# --------------------------------------------------------------------------- #
|
|
170
|
+
def _build_vapi_connector(provider_target: Any, *, conversation_direction: str) -> Any:
|
|
171
|
+
from fi.simulate.agent.definition import VapiTargetConfig
|
|
172
|
+
from fi.simulate.simulation.bridge import VapiWebSocketConnector
|
|
173
|
+
|
|
174
|
+
if isinstance(provider_target, VapiTargetConfig):
|
|
175
|
+
return VapiWebSocketConnector.from_target(
|
|
176
|
+
provider_target,
|
|
177
|
+
first_message_mode=(
|
|
178
|
+
"assistant-waits-for-user"
|
|
179
|
+
if conversation_direction == "simulator_first"
|
|
180
|
+
else "assistant-speaks-first"
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
return VapiWebSocketConnector.from_env()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _build_retell_connector(
|
|
187
|
+
provider_target: Any, *, conversation_direction: str
|
|
188
|
+
) -> Any:
|
|
189
|
+
from fi.simulate.agent.definition import RetellTargetConfig
|
|
190
|
+
from fi.simulate.simulation.bridge import RetellWebCallConnector
|
|
191
|
+
|
|
192
|
+
if isinstance(provider_target, RetellTargetConfig):
|
|
193
|
+
return RetellWebCallConnector.from_target(provider_target)
|
|
194
|
+
return RetellWebCallConnector.from_env()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# --------------------------------------------------------------------------- #
|
|
198
|
+
# built-in profiles
|
|
199
|
+
# --------------------------------------------------------------------------- #
|
|
200
|
+
_CHAT_CAPS = EndpointCapabilities(text=True, transcript_events=True, tool_events=True)
|
|
201
|
+
_WEBRTC_CAPS = EndpointCapabilities(
|
|
202
|
+
audio=True,
|
|
203
|
+
streaming=True,
|
|
204
|
+
interruption=True,
|
|
205
|
+
recording=True,
|
|
206
|
+
transcript_events=True,
|
|
207
|
+
web_rtc=True,
|
|
208
|
+
)
|
|
209
|
+
_SIP_CAPS = EndpointCapabilities(
|
|
210
|
+
audio=True,
|
|
211
|
+
streaming=True,
|
|
212
|
+
interruption=True,
|
|
213
|
+
recording=True,
|
|
214
|
+
transcript_events=True,
|
|
215
|
+
sip=True,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
_PROFILES: list[EndpointProfile] = [
|
|
219
|
+
# chat-era + actor-source target adapters ("drop in any agent", plan §4.1).
|
|
220
|
+
# config keys mirror the manifest agent: block (target/factory/args/kwargs/
|
|
221
|
+
# method/input_mode/system_prompt/...).
|
|
222
|
+
EndpointProfile(
|
|
223
|
+
AgentEndpointManifest(
|
|
224
|
+
name="callable", provider="callable",
|
|
225
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
226
|
+
),
|
|
227
|
+
target_resolver=resolve_python_callable,
|
|
228
|
+
),
|
|
229
|
+
EndpointProfile(
|
|
230
|
+
AgentEndpointManifest(
|
|
231
|
+
name="python_callable", provider="callable",
|
|
232
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
233
|
+
),
|
|
234
|
+
target_resolver=resolve_python_callable,
|
|
235
|
+
),
|
|
236
|
+
EndpointProfile(
|
|
237
|
+
AgentEndpointManifest(
|
|
238
|
+
name="import_object", provider="python",
|
|
239
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
240
|
+
),
|
|
241
|
+
target_resolver=resolve_import_object,
|
|
242
|
+
),
|
|
243
|
+
EndpointProfile(
|
|
244
|
+
AgentEndpointManifest(
|
|
245
|
+
name="factory", provider="python",
|
|
246
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
247
|
+
),
|
|
248
|
+
target_resolver=resolve_factory,
|
|
249
|
+
),
|
|
250
|
+
EndpointProfile(
|
|
251
|
+
AgentEndpointManifest(
|
|
252
|
+
name="framework", provider="framework",
|
|
253
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
254
|
+
),
|
|
255
|
+
target_resolver=resolve_framework,
|
|
256
|
+
),
|
|
257
|
+
EndpointProfile(
|
|
258
|
+
AgentEndpointManifest(
|
|
259
|
+
name="system_prompt", provider="llm",
|
|
260
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
261
|
+
),
|
|
262
|
+
target_resolver=resolve_system_prompt,
|
|
263
|
+
runs_caller_code=False,
|
|
264
|
+
),
|
|
265
|
+
EndpointProfile(
|
|
266
|
+
AgentEndpointManifest(
|
|
267
|
+
name="http", provider="http",
|
|
268
|
+
world_kinds=["chat", "text"], capabilities=_CHAT_CAPS,
|
|
269
|
+
),
|
|
270
|
+
target_resolver=resolve_http,
|
|
271
|
+
runs_caller_code=False,
|
|
272
|
+
),
|
|
273
|
+
EndpointProfile(
|
|
274
|
+
AgentEndpointManifest(
|
|
275
|
+
name="websocket", provider="websocket",
|
|
276
|
+
world_kinds=["chat", "text"],
|
|
277
|
+
capabilities=EndpointCapabilities(
|
|
278
|
+
text=True, streaming=True, transcript_events=True, tool_events=True
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
),
|
|
282
|
+
# voice target adapters (keyed by transport.kind)
|
|
283
|
+
EndpointProfile(
|
|
284
|
+
AgentEndpointManifest(
|
|
285
|
+
name="webrtc", provider="livekit",
|
|
286
|
+
world_kinds=["voice"], capabilities=_WEBRTC_CAPS,
|
|
287
|
+
)
|
|
288
|
+
),
|
|
289
|
+
EndpointProfile(
|
|
290
|
+
AgentEndpointManifest(
|
|
291
|
+
name="livekit", provider="livekit",
|
|
292
|
+
world_kinds=["voice"], capabilities=_WEBRTC_CAPS,
|
|
293
|
+
)
|
|
294
|
+
),
|
|
295
|
+
EndpointProfile(
|
|
296
|
+
AgentEndpointManifest(
|
|
297
|
+
name="vapi_websocket", provider="vapi",
|
|
298
|
+
world_kinds=["voice"], capabilities=_WEBRTC_CAPS,
|
|
299
|
+
),
|
|
300
|
+
uses_web_audio_bridge=True,
|
|
301
|
+
bridge_provider="vapi",
|
|
302
|
+
evidence_provider="vapi",
|
|
303
|
+
required_env_rule=_vapi_web_required_env,
|
|
304
|
+
connector_builder=_build_vapi_connector,
|
|
305
|
+
),
|
|
306
|
+
EndpointProfile(
|
|
307
|
+
AgentEndpointManifest(
|
|
308
|
+
name="retell_webcall", provider="retell",
|
|
309
|
+
world_kinds=["voice"], capabilities=_WEBRTC_CAPS,
|
|
310
|
+
),
|
|
311
|
+
uses_web_audio_bridge=True,
|
|
312
|
+
bridge_provider="retell",
|
|
313
|
+
evidence_provider="retell",
|
|
314
|
+
required_env_rule=_retell_web_required_env,
|
|
315
|
+
connector_builder=_build_retell_connector,
|
|
316
|
+
),
|
|
317
|
+
EndpointProfile(
|
|
318
|
+
AgentEndpointManifest(
|
|
319
|
+
name="sip_outbound", provider="sip",
|
|
320
|
+
world_kinds=["voice"], capabilities=_SIP_CAPS,
|
|
321
|
+
),
|
|
322
|
+
is_sip=True,
|
|
323
|
+
places_outbound_call=True,
|
|
324
|
+
),
|
|
325
|
+
EndpointProfile(
|
|
326
|
+
AgentEndpointManifest(
|
|
327
|
+
name="sip_inbound", provider="sip",
|
|
328
|
+
world_kinds=["voice"], capabilities=_SIP_CAPS,
|
|
329
|
+
),
|
|
330
|
+
is_sip=True,
|
|
331
|
+
receives_inbound_call=True,
|
|
332
|
+
required_env_rule=_sip_inbound_required_env,
|
|
333
|
+
),
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
for _profile in _PROFILES:
|
|
337
|
+
register_endpoint(_profile.name, _profile)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def get_profile(name: str) -> Optional[EndpointProfile]:
|
|
341
|
+
"""Return the registered profile for a target adapter name, or ``None``."""
|
|
342
|
+
from fi.simulate.registry import endpoint_registry
|
|
343
|
+
|
|
344
|
+
value = endpoint_registry.get_or_none(name)
|
|
345
|
+
return value if isinstance(value, EndpointProfile) else None
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
__all__ = ["EndpointProfile", "get_profile"]
|