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,473 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Span Processors.
|
|
3
|
+
|
|
4
|
+
Foundation classes for span processing in the OTEL integration.
|
|
5
|
+
Processors can filter, enrich, and transform spans before export.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any, Callable, List, Optional, Dict
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from opentelemetry.sdk.trace import SpanProcessor, ReadableSpan
|
|
15
|
+
from opentelemetry.context import Context
|
|
16
|
+
OTEL_AVAILABLE = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
OTEL_AVAILABLE = False
|
|
19
|
+
# Define stub types for when OTEL is not installed
|
|
20
|
+
SpanProcessor = object
|
|
21
|
+
ReadableSpan = Any
|
|
22
|
+
Context = Any
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BaseSpanProcessor(ABC):
|
|
28
|
+
"""
|
|
29
|
+
Base class for custom span processors.
|
|
30
|
+
|
|
31
|
+
Span processors intercept spans at various lifecycle points:
|
|
32
|
+
- on_start: When a span is started
|
|
33
|
+
- on_end: When a span is ended
|
|
34
|
+
|
|
35
|
+
Subclasses can enrich spans with additional attributes,
|
|
36
|
+
filter spans, or perform side effects like evaluation.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
class MyProcessor(BaseSpanProcessor):
|
|
40
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
41
|
+
if self.should_process(span):
|
|
42
|
+
self.enrich_span(span)
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, enabled: bool = True):
|
|
46
|
+
"""
|
|
47
|
+
Initialize the processor.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
enabled: Whether this processor is active
|
|
51
|
+
"""
|
|
52
|
+
self._enabled = enabled
|
|
53
|
+
self._shutdown = False
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def enabled(self) -> bool:
|
|
57
|
+
"""Check if processor is enabled."""
|
|
58
|
+
return self._enabled and not self._shutdown
|
|
59
|
+
|
|
60
|
+
def enable(self) -> None:
|
|
61
|
+
"""Enable the processor."""
|
|
62
|
+
self._enabled = True
|
|
63
|
+
|
|
64
|
+
def disable(self) -> None:
|
|
65
|
+
"""Disable the processor."""
|
|
66
|
+
self._enabled = False
|
|
67
|
+
|
|
68
|
+
def on_start(
|
|
69
|
+
self,
|
|
70
|
+
span: Any,
|
|
71
|
+
parent_context: Optional[Context] = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Called when a span is started.
|
|
75
|
+
|
|
76
|
+
Override this to add attributes at span start time.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
span: The span that was started
|
|
80
|
+
parent_context: The parent context (if any)
|
|
81
|
+
"""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Called when a span is ended.
|
|
88
|
+
|
|
89
|
+
This is the main processing hook. Override to add
|
|
90
|
+
evaluation scores, cost data, or other enrichment.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
span: The completed span (read-only)
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def shutdown(self) -> None:
|
|
98
|
+
"""
|
|
99
|
+
Shutdown the processor.
|
|
100
|
+
|
|
101
|
+
Called when the tracer provider is shutting down.
|
|
102
|
+
Override to cleanup resources.
|
|
103
|
+
"""
|
|
104
|
+
self._shutdown = True
|
|
105
|
+
|
|
106
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
107
|
+
"""
|
|
108
|
+
Force flush any buffered data.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
timeout_millis: Maximum time to wait
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
True if flush succeeded
|
|
115
|
+
"""
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
def should_process(self, span: ReadableSpan) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Determine if this span should be processed.
|
|
121
|
+
|
|
122
|
+
Override to implement custom filtering logic.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
span: The span to check
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if span should be processed
|
|
129
|
+
"""
|
|
130
|
+
return self.enabled
|
|
131
|
+
|
|
132
|
+
def get_span_attribute(
|
|
133
|
+
self,
|
|
134
|
+
span: ReadableSpan,
|
|
135
|
+
key: str,
|
|
136
|
+
default: Any = None,
|
|
137
|
+
) -> Any:
|
|
138
|
+
"""
|
|
139
|
+
Safely get an attribute from a span.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
span: The span
|
|
143
|
+
key: Attribute key
|
|
144
|
+
default: Default value if not found
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Attribute value or default
|
|
148
|
+
"""
|
|
149
|
+
if not OTEL_AVAILABLE:
|
|
150
|
+
return default
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
attrs = span.attributes or {}
|
|
154
|
+
return attrs.get(key, default)
|
|
155
|
+
except Exception:
|
|
156
|
+
return default
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class FilteringSpanProcessor(BaseSpanProcessor):
|
|
160
|
+
"""
|
|
161
|
+
A processor that filters spans based on criteria.
|
|
162
|
+
|
|
163
|
+
Only processes spans that match the filter function.
|
|
164
|
+
Useful for processing only LLM spans, only certain models, etc.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
# Only process OpenAI spans
|
|
168
|
+
processor = FilteringSpanProcessor(
|
|
169
|
+
filter_fn=lambda span: span.attributes.get("gen_ai.system") == "openai",
|
|
170
|
+
delegate=MyEnrichmentProcessor(),
|
|
171
|
+
)
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def __init__(
|
|
175
|
+
self,
|
|
176
|
+
filter_fn: Callable[[ReadableSpan], bool],
|
|
177
|
+
delegate: Optional[BaseSpanProcessor] = None,
|
|
178
|
+
enabled: bool = True,
|
|
179
|
+
):
|
|
180
|
+
"""
|
|
181
|
+
Initialize the filtering processor.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
filter_fn: Function that returns True for spans to process
|
|
185
|
+
delegate: Optional processor to delegate to for matching spans
|
|
186
|
+
enabled: Whether this processor is active
|
|
187
|
+
"""
|
|
188
|
+
super().__init__(enabled=enabled)
|
|
189
|
+
self._filter_fn = filter_fn
|
|
190
|
+
self._delegate = delegate
|
|
191
|
+
|
|
192
|
+
def should_process(self, span: ReadableSpan) -> bool:
|
|
193
|
+
"""Check if span passes the filter."""
|
|
194
|
+
if not self.enabled:
|
|
195
|
+
return False
|
|
196
|
+
try:
|
|
197
|
+
return self._filter_fn(span)
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.warning(f"Filter function error: {e}")
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
203
|
+
"""Process span if it passes the filter."""
|
|
204
|
+
if self.should_process(span) and self._delegate:
|
|
205
|
+
self._delegate.on_end(span)
|
|
206
|
+
|
|
207
|
+
def shutdown(self) -> None:
|
|
208
|
+
"""Shutdown delegate processor."""
|
|
209
|
+
super().shutdown()
|
|
210
|
+
if self._delegate:
|
|
211
|
+
self._delegate.shutdown()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class CompositeSpanProcessor(BaseSpanProcessor):
|
|
215
|
+
"""
|
|
216
|
+
A processor that combines multiple processors.
|
|
217
|
+
|
|
218
|
+
Runs all child processors on each span. Can run processors
|
|
219
|
+
in sequence or parallel.
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
processor = CompositeSpanProcessor([
|
|
223
|
+
LLMSpanProcessor(),
|
|
224
|
+
EvaluationSpanProcessor(metrics=["relevance"]),
|
|
225
|
+
CostSpanProcessor(),
|
|
226
|
+
])
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def __init__(
|
|
230
|
+
self,
|
|
231
|
+
processors: List[BaseSpanProcessor],
|
|
232
|
+
parallel: bool = False,
|
|
233
|
+
max_workers: int = 4,
|
|
234
|
+
enabled: bool = True,
|
|
235
|
+
):
|
|
236
|
+
"""
|
|
237
|
+
Initialize the composite processor.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
processors: List of processors to run
|
|
241
|
+
parallel: Whether to run processors in parallel
|
|
242
|
+
max_workers: Max threads for parallel execution
|
|
243
|
+
enabled: Whether this processor is active
|
|
244
|
+
"""
|
|
245
|
+
super().__init__(enabled=enabled)
|
|
246
|
+
self._processors = processors
|
|
247
|
+
self._parallel = parallel
|
|
248
|
+
self._max_workers = max_workers
|
|
249
|
+
self._executor: Optional[ThreadPoolExecutor] = None
|
|
250
|
+
|
|
251
|
+
def add_processor(self, processor: BaseSpanProcessor) -> None:
|
|
252
|
+
"""Add a processor to the composite."""
|
|
253
|
+
self._processors.append(processor)
|
|
254
|
+
|
|
255
|
+
def remove_processor(self, processor: BaseSpanProcessor) -> None:
|
|
256
|
+
"""Remove a processor from the composite."""
|
|
257
|
+
if processor in self._processors:
|
|
258
|
+
self._processors.remove(processor)
|
|
259
|
+
|
|
260
|
+
def on_start(
|
|
261
|
+
self,
|
|
262
|
+
span: Any,
|
|
263
|
+
parent_context: Optional[Context] = None,
|
|
264
|
+
) -> None:
|
|
265
|
+
"""Call on_start on all child processors."""
|
|
266
|
+
if not self.enabled:
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
for processor in self._processors:
|
|
270
|
+
try:
|
|
271
|
+
processor.on_start(span, parent_context)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.warning(f"Processor on_start error: {e}")
|
|
274
|
+
|
|
275
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
276
|
+
"""Call on_end on all child processors."""
|
|
277
|
+
if not self.enabled:
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
if self._parallel:
|
|
281
|
+
self._process_parallel(span)
|
|
282
|
+
else:
|
|
283
|
+
self._process_sequential(span)
|
|
284
|
+
|
|
285
|
+
def _process_sequential(self, span: ReadableSpan) -> None:
|
|
286
|
+
"""Process span through all processors sequentially."""
|
|
287
|
+
for processor in self._processors:
|
|
288
|
+
try:
|
|
289
|
+
if processor.enabled:
|
|
290
|
+
processor.on_end(span)
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.warning(f"Processor on_end error: {e}")
|
|
293
|
+
|
|
294
|
+
def _process_parallel(self, span: ReadableSpan) -> None:
|
|
295
|
+
"""Process span through all processors in parallel."""
|
|
296
|
+
if self._executor is None:
|
|
297
|
+
self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
|
|
298
|
+
|
|
299
|
+
def run_processor(processor: BaseSpanProcessor) -> None:
|
|
300
|
+
try:
|
|
301
|
+
if processor.enabled:
|
|
302
|
+
processor.on_end(span)
|
|
303
|
+
except Exception as e:
|
|
304
|
+
logger.warning(f"Processor on_end error: {e}")
|
|
305
|
+
|
|
306
|
+
futures = [
|
|
307
|
+
self._executor.submit(run_processor, p)
|
|
308
|
+
for p in self._processors
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
# Wait for all to complete
|
|
312
|
+
for future in futures:
|
|
313
|
+
try:
|
|
314
|
+
future.result(timeout=30)
|
|
315
|
+
except Exception as e:
|
|
316
|
+
logger.warning(f"Parallel processor error: {e}")
|
|
317
|
+
|
|
318
|
+
def shutdown(self) -> None:
|
|
319
|
+
"""Shutdown all child processors."""
|
|
320
|
+
super().shutdown()
|
|
321
|
+
|
|
322
|
+
for processor in self._processors:
|
|
323
|
+
try:
|
|
324
|
+
processor.shutdown()
|
|
325
|
+
except Exception as e:
|
|
326
|
+
logger.warning(f"Processor shutdown error: {e}")
|
|
327
|
+
|
|
328
|
+
if self._executor:
|
|
329
|
+
self._executor.shutdown(wait=True)
|
|
330
|
+
self._executor = None
|
|
331
|
+
|
|
332
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
333
|
+
"""Force flush all child processors."""
|
|
334
|
+
success = True
|
|
335
|
+
per_processor_timeout = timeout_millis // max(len(self._processors), 1)
|
|
336
|
+
|
|
337
|
+
for processor in self._processors:
|
|
338
|
+
try:
|
|
339
|
+
if not processor.force_flush(per_processor_timeout):
|
|
340
|
+
success = False
|
|
341
|
+
except Exception as e:
|
|
342
|
+
logger.warning(f"Processor force_flush error: {e}")
|
|
343
|
+
success = False
|
|
344
|
+
|
|
345
|
+
return success
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class AttributeEnrichmentProcessor(BaseSpanProcessor):
|
|
349
|
+
"""
|
|
350
|
+
A processor that adds static attributes to spans.
|
|
351
|
+
|
|
352
|
+
Useful for adding deployment info, version numbers, or
|
|
353
|
+
other metadata to all spans.
|
|
354
|
+
|
|
355
|
+
Example:
|
|
356
|
+
processor = AttributeEnrichmentProcessor({
|
|
357
|
+
"deployment.environment": "production",
|
|
358
|
+
"service.version": "1.2.3",
|
|
359
|
+
"team": "ml-platform",
|
|
360
|
+
})
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
def __init__(
|
|
364
|
+
self,
|
|
365
|
+
attributes: Dict[str, Any],
|
|
366
|
+
enabled: bool = True,
|
|
367
|
+
):
|
|
368
|
+
"""
|
|
369
|
+
Initialize with attributes to add.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
attributes: Static attributes to add to all spans
|
|
373
|
+
enabled: Whether this processor is active
|
|
374
|
+
"""
|
|
375
|
+
super().__init__(enabled=enabled)
|
|
376
|
+
self._attributes = attributes
|
|
377
|
+
|
|
378
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
379
|
+
"""Add attributes to span (Note: spans are read-only at end)."""
|
|
380
|
+
# Note: In OTEL, spans are read-only after ending.
|
|
381
|
+
# This processor is mainly useful for on_start enrichment.
|
|
382
|
+
# For on_end, we'd need to use span events or a different approach.
|
|
383
|
+
pass
|
|
384
|
+
|
|
385
|
+
def on_start(
|
|
386
|
+
self,
|
|
387
|
+
span: Any,
|
|
388
|
+
parent_context: Optional[Context] = None,
|
|
389
|
+
) -> None:
|
|
390
|
+
"""Add attributes when span starts."""
|
|
391
|
+
if not self.enabled:
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
try:
|
|
395
|
+
for key, value in self._attributes.items():
|
|
396
|
+
span.set_attribute(key, value)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
logger.warning(f"Failed to set attributes: {e}")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class ConditionalProcessor(BaseSpanProcessor):
|
|
402
|
+
"""
|
|
403
|
+
A processor that conditionally delegates based on span attributes.
|
|
404
|
+
|
|
405
|
+
Allows different processing paths for different span types.
|
|
406
|
+
|
|
407
|
+
Example:
|
|
408
|
+
processor = ConditionalProcessor(
|
|
409
|
+
conditions=[
|
|
410
|
+
(lambda s: s.attributes.get("gen_ai.system") == "openai", openai_processor),
|
|
411
|
+
(lambda s: s.attributes.get("gen_ai.system") == "anthropic", anthropic_processor),
|
|
412
|
+
],
|
|
413
|
+
default=generic_processor,
|
|
414
|
+
)
|
|
415
|
+
"""
|
|
416
|
+
|
|
417
|
+
def __init__(
|
|
418
|
+
self,
|
|
419
|
+
conditions: List[tuple[Callable[[ReadableSpan], bool], BaseSpanProcessor]],
|
|
420
|
+
default: Optional[BaseSpanProcessor] = None,
|
|
421
|
+
enabled: bool = True,
|
|
422
|
+
):
|
|
423
|
+
"""
|
|
424
|
+
Initialize with conditions and delegates.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
conditions: List of (condition, processor) tuples
|
|
428
|
+
default: Default processor if no conditions match
|
|
429
|
+
enabled: Whether this processor is active
|
|
430
|
+
"""
|
|
431
|
+
super().__init__(enabled=enabled)
|
|
432
|
+
self._conditions = conditions
|
|
433
|
+
self._default = default
|
|
434
|
+
|
|
435
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
436
|
+
"""Route span to appropriate processor."""
|
|
437
|
+
if not self.enabled:
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
for condition_fn, processor in self._conditions:
|
|
441
|
+
try:
|
|
442
|
+
if condition_fn(span):
|
|
443
|
+
processor.on_end(span)
|
|
444
|
+
return
|
|
445
|
+
except Exception as e:
|
|
446
|
+
logger.warning(f"Condition check error: {e}")
|
|
447
|
+
|
|
448
|
+
# No condition matched, use default
|
|
449
|
+
if self._default:
|
|
450
|
+
self._default.on_end(span)
|
|
451
|
+
|
|
452
|
+
def shutdown(self) -> None:
|
|
453
|
+
"""Shutdown all delegate processors."""
|
|
454
|
+
super().shutdown()
|
|
455
|
+
|
|
456
|
+
for _, processor in self._conditions:
|
|
457
|
+
try:
|
|
458
|
+
processor.shutdown()
|
|
459
|
+
except Exception as e:
|
|
460
|
+
logger.warning(f"Processor shutdown error: {e}")
|
|
461
|
+
|
|
462
|
+
if self._default:
|
|
463
|
+
self._default.shutdown()
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
__all__ = [
|
|
467
|
+
"BaseSpanProcessor",
|
|
468
|
+
"FilteringSpanProcessor",
|
|
469
|
+
"CompositeSpanProcessor",
|
|
470
|
+
"AttributeEnrichmentProcessor",
|
|
471
|
+
"ConditionalProcessor",
|
|
472
|
+
"OTEL_AVAILABLE",
|
|
473
|
+
]
|