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,193 @@
|
|
|
1
|
+
"""Keyed account-sync client over fi-instrumentation-otel (Phase 8, ARCH §2d).
|
|
2
|
+
|
|
3
|
+
THE ONLY SANCTIONED NETWORK HOME in the telemetry package — the
|
|
4
|
+
``telemetry_boundary`` gate scans every other telemetry module for
|
|
5
|
+
network-capable imports. ``fi_instrumentation`` is imported LAZILY inside
|
|
6
|
+
``sync_run`` only, after the kill switch and the key check, so the no-key
|
|
7
|
+
import graph carries nothing network-capable.
|
|
8
|
+
|
|
9
|
+
Doctrine (never soften): keys are the consent boundary (P8-D2) — they gate
|
|
10
|
+
the DESTINATION, never the capability; metadata by default; content only with
|
|
11
|
+
the capture+redaction contract; ``AGENT_LEARNING_TELEMETRY=off`` overrides
|
|
12
|
+
keys and binds everything including vendored ``fi/*`` (P8-D6); any transport
|
|
13
|
+
failure degrades to local-only with the cursor unmoved (R§3.5).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
from typing import Any, Mapping
|
|
21
|
+
|
|
22
|
+
from ..config import DEFAULT_API_URL, AgentLearningConfig
|
|
23
|
+
from ._contract import kill_switch_on
|
|
24
|
+
from ._ledger import RunLedger
|
|
25
|
+
from ._row import canonical_row_address
|
|
26
|
+
|
|
27
|
+
SYNC_PATH = "/tracer/v1/traces"
|
|
28
|
+
LEDGER_ROW_ATTR = "fi.kit.ledger_row"
|
|
29
|
+
SYNC_SPAN_NAME = "agent-learning.ledger-row"
|
|
30
|
+
SYNC_PROJECT_NAME = "agent-learning"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def sync_enabled() -> bool:
|
|
34
|
+
"""Keys present AND the kill switch not ``off`` (P8-D2 + P8-D6)."""
|
|
35
|
+
|
|
36
|
+
if kill_switch_on():
|
|
37
|
+
return False
|
|
38
|
+
config = AgentLearningConfig.from_env()
|
|
39
|
+
return bool(config.api_key and config.secret_key)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def sync_destination() -> dict[str, Any]:
|
|
43
|
+
"""The literal destination a real sync would use — names always, values
|
|
44
|
+
never (the dry-run transparency surface prints this, UI-UX §4.1)."""
|
|
45
|
+
|
|
46
|
+
config = AgentLearningConfig.from_env()
|
|
47
|
+
base_url = (
|
|
48
|
+
os.environ.get("FI_BASE_URL") or config.api_url or DEFAULT_API_URL
|
|
49
|
+
).rstrip("/")
|
|
50
|
+
return {
|
|
51
|
+
"endpoint": f"{base_url}{SYNC_PATH}",
|
|
52
|
+
"base_url": base_url,
|
|
53
|
+
"transport": "otlp-http",
|
|
54
|
+
"headers": {
|
|
55
|
+
"X-Api-Key": "present" if config.api_key else "missing",
|
|
56
|
+
"X-Secret-Key": "present" if config.secret_key else "missing",
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def encode_metadata_row(row: Mapping[str, Any]) -> dict[str, Any]:
|
|
62
|
+
"""The metadata-only sync payload (PRD §4.2): the canonical row fields —
|
|
63
|
+
metadata, hashes, and the contract fields themselves. Content
|
|
64
|
+
(transcripts/prompts/tool I/O) is NEVER part of this projection."""
|
|
65
|
+
|
|
66
|
+
return {key: value for key, value in row.items()}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def encoded_run_id(row: Mapping[str, Any]) -> str:
|
|
70
|
+
"""The content address the sync encoder transmits — must be IDENTICAL to
|
|
71
|
+
the locally-computed ``run_id`` (gate #72 check 6, identity equivalence).
|
|
72
|
+
``canonical_row_address`` strips the envelope fields, so the encoder and
|
|
73
|
+
the ledger share one canonicalization discipline."""
|
|
74
|
+
|
|
75
|
+
return canonical_row_address(encode_metadata_row(row))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def content_sync_admissible(row: Mapping[str, Any]) -> bool:
|
|
79
|
+
"""Row-level content gate: ``content_bearing`` rows need the non-empty
|
|
80
|
+
``redaction`` mapping (names + strategy) recorded on the row."""
|
|
81
|
+
|
|
82
|
+
redaction = row.get("redaction")
|
|
83
|
+
return bool(
|
|
84
|
+
row.get("content_bearing") is True
|
|
85
|
+
and isinstance(redaction, Mapping)
|
|
86
|
+
and redaction
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _collector_reachable(base_url: str, timeout: float = 3.0) -> tuple[bool, str]:
|
|
91
|
+
"""Cheap TCP preflight so an unreachable collector degrades to local
|
|
92
|
+
immediately (the OTLP exporter itself fire-and-forgets failures). Lazy
|
|
93
|
+
stdlib imports: this module is the only sanctioned network home, and even
|
|
94
|
+
here network-capable imports stay in-function (gate #72 check 1)."""
|
|
95
|
+
|
|
96
|
+
import socket
|
|
97
|
+
from urllib.parse import urlparse
|
|
98
|
+
|
|
99
|
+
parsed = urlparse(base_url)
|
|
100
|
+
host = parsed.hostname or base_url
|
|
101
|
+
port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
|
102
|
+
try:
|
|
103
|
+
with socket.create_connection((host, port), timeout=timeout):
|
|
104
|
+
return True, "ok"
|
|
105
|
+
except OSError as exc:
|
|
106
|
+
return False, f"collector unreachable: {exc}"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def sync_run(
|
|
110
|
+
row: Mapping[str, Any],
|
|
111
|
+
*,
|
|
112
|
+
content: bool = False,
|
|
113
|
+
ledger: RunLedger | None = None,
|
|
114
|
+
world: str | None = None,
|
|
115
|
+
) -> dict[str, Any]:
|
|
116
|
+
"""Sync one ledger row to the user's own Future AGI account.
|
|
117
|
+
|
|
118
|
+
Idempotent by content address (P8-D3): a re-send of an already-confirmed
|
|
119
|
+
``run_id`` is a no-op. Every failure degrades to local-only — the row
|
|
120
|
+
stays unsynced, the cursor unmoved, and nothing propagates (R§3.5).
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
run_id = str(row.get("run_id") or "")
|
|
124
|
+
if kill_switch_on():
|
|
125
|
+
return {"status": "disabled", "reason": "kill_switch", "sent": False}
|
|
126
|
+
config = AgentLearningConfig.from_env()
|
|
127
|
+
if not (config.api_key and config.secret_key):
|
|
128
|
+
return {"status": "no_keys", "sent": False}
|
|
129
|
+
channel = "metadata"
|
|
130
|
+
if content:
|
|
131
|
+
if not content_sync_admissible(row):
|
|
132
|
+
return {
|
|
133
|
+
"status": "refused",
|
|
134
|
+
"reason": "capture_contract_missing",
|
|
135
|
+
"sent": False,
|
|
136
|
+
}
|
|
137
|
+
channel = "metadata+content"
|
|
138
|
+
store = ledger if ledger is not None else RunLedger()
|
|
139
|
+
cursor = store.read_cursor()
|
|
140
|
+
already = cursor["synced"].get(run_id)
|
|
141
|
+
if already == channel or (already == "metadata+content" and channel == "metadata"):
|
|
142
|
+
return {
|
|
143
|
+
"status": "noop",
|
|
144
|
+
"reason": "already_synced",
|
|
145
|
+
"channel": already,
|
|
146
|
+
"sent": False,
|
|
147
|
+
}
|
|
148
|
+
payload = encode_metadata_row(row)
|
|
149
|
+
destination = sync_destination()
|
|
150
|
+
reachable, reason = _collector_reachable(destination["base_url"])
|
|
151
|
+
if not reachable:
|
|
152
|
+
# Degrade-to-local (R§3.5): the row stays unsynced, the cursor
|
|
153
|
+
# unmoved; a later `runs sync --queued` resumes idempotently.
|
|
154
|
+
return {"status": "deferred", "reason": reason, "sent": False}
|
|
155
|
+
# Emit via the export-result-aware emitter (Phase 14): the OTLP HTTP exporter
|
|
156
|
+
# SWALLOWS export failures (it returns SpanExportResult.FAILURE without
|
|
157
|
+
# raising), so the old register(...) path reported `synced` while a 401'd span
|
|
158
|
+
# never landed. `keyed_emit` reports `synced` only on an OBSERVED export
|
|
159
|
+
# success — anything else degrades to local with the cursor UNMOVED (R§3.5).
|
|
160
|
+
from . import _emit # lazy: network-capable import after every gate
|
|
161
|
+
|
|
162
|
+
emit = _emit.keyed_emit(
|
|
163
|
+
span_name=SYNC_SPAN_NAME,
|
|
164
|
+
root_attrs={
|
|
165
|
+
LEDGER_ROW_ATTR: json.dumps(
|
|
166
|
+
payload, sort_keys=True, separators=(",", ":"), default=str
|
|
167
|
+
)
|
|
168
|
+
},
|
|
169
|
+
project_name=SYNC_PROJECT_NAME,
|
|
170
|
+
headers={
|
|
171
|
+
"X-Api-Key": config.api_key,
|
|
172
|
+
"X-Secret-Key": config.secret_key,
|
|
173
|
+
},
|
|
174
|
+
run_id=run_id,
|
|
175
|
+
phase=str(row.get("phase") or ""),
|
|
176
|
+
world=world,
|
|
177
|
+
)
|
|
178
|
+
if emit["status"] != "synced":
|
|
179
|
+
# export rejected (e.g. 401) or transport failure → cursor unmoved; a
|
|
180
|
+
# later `runs sync --queued` resumes idempotently (R§3.5).
|
|
181
|
+
return {
|
|
182
|
+
"status": emit["status"],
|
|
183
|
+
"reason": emit.get("reason"),
|
|
184
|
+
"sent": False,
|
|
185
|
+
}
|
|
186
|
+
store.write_cursor(run_id, channel)
|
|
187
|
+
return {
|
|
188
|
+
"status": "synced",
|
|
189
|
+
"channel": channel,
|
|
190
|
+
"run_id": run_id,
|
|
191
|
+
"endpoint": destination["endpoint"],
|
|
192
|
+
"sent": True,
|
|
193
|
+
}
|
fi/alk/telemetry/_url.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Dashboard URL construction (Phase 14, ARCH §3) — the W&B "View run at …" link.
|
|
2
|
+
|
|
3
|
+
Route shapes are VERIFIED from the platform repo (RESEARCH §2.2), not guessed:
|
|
4
|
+
observe project /dashboard/observe/<project_id>
|
|
5
|
+
observe trace /dashboard/observe/<project_id>/trace/<trace_id>
|
|
6
|
+
App host inverts the verified ``api.→app.`` map (config-global.js:8).
|
|
7
|
+
|
|
8
|
+
Honesty rule (RESEARCH §2): never return a constructed *deep-link* unless we have
|
|
9
|
+
a real project id (from config or a successful name→id resolution). Otherwise
|
|
10
|
+
return the project-list view and LABEL it a fallback — a link that lands on a real
|
|
11
|
+
page beats a deep-link to an empty/404 view (the false-``synced`` failure mode).
|
|
12
|
+
|
|
13
|
+
Network-capable imports (``requests``) stay in-function (gate #72).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from ..config import DEFAULT_API_URL, AgentLearningConfig
|
|
21
|
+
|
|
22
|
+
_RESOLVE_PATH = "/tracer/project/list_projects/"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _app_host(base_url: str) -> tuple[str, bool]:
|
|
26
|
+
"""Return (app_base, is_known_app_host). For the managed cloud, api.* → app.*.
|
|
27
|
+
For a self-hosted/collector base, return it unchanged (we do not invent an
|
|
28
|
+
app host that may not exist)."""
|
|
29
|
+
|
|
30
|
+
from urllib.parse import urlparse # lazy: this is a sanctioned network home
|
|
31
|
+
|
|
32
|
+
parsed = urlparse(base_url)
|
|
33
|
+
host = parsed.hostname or ""
|
|
34
|
+
if host.startswith("api."):
|
|
35
|
+
app_host = "app." + host[len("api."):]
|
|
36
|
+
return f"{parsed.scheme}://{app_host}", True
|
|
37
|
+
# unknown / self-hosted: keep the base as the (best-effort) UI host
|
|
38
|
+
return base_url.rstrip("/"), False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def fmt_trace_id(trace_id_hex: str) -> str:
|
|
42
|
+
"""Format a 32-char hex trace id to the dashed form the dashboard routes on
|
|
43
|
+
(``default.spans.trace_id`` is stored dashed per the CH25 trace migration).
|
|
44
|
+
The exact format is pinned empirically in the live step; this is the standard
|
|
45
|
+
UUID dashing of the 128-bit id."""
|
|
46
|
+
|
|
47
|
+
h = (trace_id_hex or "").strip().lower().replace("-", "")
|
|
48
|
+
if len(h) != 32:
|
|
49
|
+
return trace_id_hex
|
|
50
|
+
return f"{h[0:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:32]}"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _resolve_project_id(project_name: str, config: AgentLearningConfig) -> str | None:
|
|
54
|
+
"""Best-effort name→id via an authenticated GET. Any failure → None (caller
|
|
55
|
+
falls back). Never raises into the run."""
|
|
56
|
+
|
|
57
|
+
if not (config.api_key and config.secret_key):
|
|
58
|
+
return None
|
|
59
|
+
try:
|
|
60
|
+
import requests
|
|
61
|
+
|
|
62
|
+
base = (config.api_url or DEFAULT_API_URL).rstrip("/")
|
|
63
|
+
resp = requests.get(
|
|
64
|
+
f"{base}{_RESOLVE_PATH}",
|
|
65
|
+
headers={"X-Api-Key": config.api_key, "X-Secret-Key": config.secret_key},
|
|
66
|
+
timeout=5.0,
|
|
67
|
+
)
|
|
68
|
+
if resp.status_code != 200:
|
|
69
|
+
return None
|
|
70
|
+
data = resp.json()
|
|
71
|
+
rows = data.get("result") or data.get("results") or data.get("projects") or data
|
|
72
|
+
if isinstance(rows, dict):
|
|
73
|
+
rows = rows.get("projects") or rows.get("result") or []
|
|
74
|
+
for row in rows or []:
|
|
75
|
+
if not isinstance(row, dict):
|
|
76
|
+
continue
|
|
77
|
+
if str(row.get("name") or row.get("project_name") or "") == project_name:
|
|
78
|
+
pid = row.get("id") or row.get("project_id") or row.get("uuid")
|
|
79
|
+
if pid:
|
|
80
|
+
return str(pid)
|
|
81
|
+
except Exception: # noqa: BLE001 — resolution is best-effort, degrade to fallback
|
|
82
|
+
return None
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def build_dashboard_url(
|
|
87
|
+
project_name: str,
|
|
88
|
+
trace_id_hex: str | None,
|
|
89
|
+
*,
|
|
90
|
+
config: AgentLearningConfig | None = None,
|
|
91
|
+
) -> dict[str, Any]:
|
|
92
|
+
"""Construct the dashboard URL for a keyed run. Returns
|
|
93
|
+
``{url, kind, project_name}`` where ``kind`` ∈
|
|
94
|
+
{``deep_link``, ``project``, ``list_fallback``}."""
|
|
95
|
+
|
|
96
|
+
config = config or AgentLearningConfig.from_env()
|
|
97
|
+
base_url = (config.api_url or DEFAULT_API_URL).rstrip("/")
|
|
98
|
+
app_base, _known = _app_host(base_url)
|
|
99
|
+
|
|
100
|
+
# project id: explicit config first (no network), then best-effort resolution.
|
|
101
|
+
project_id = config.project_id or _resolve_project_id(project_name, config)
|
|
102
|
+
|
|
103
|
+
if project_id and trace_id_hex:
|
|
104
|
+
return {
|
|
105
|
+
"url": f"{app_base}/dashboard/observe/{project_id}/trace/{fmt_trace_id(trace_id_hex)}",
|
|
106
|
+
"kind": "deep_link",
|
|
107
|
+
"project_name": project_name,
|
|
108
|
+
}
|
|
109
|
+
if project_id:
|
|
110
|
+
return {
|
|
111
|
+
"url": f"{app_base}/dashboard/observe/{project_id}",
|
|
112
|
+
"kind": "project",
|
|
113
|
+
"project_name": project_name,
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
"url": f"{app_base}/dashboard/observe",
|
|
117
|
+
"kind": "list_fallback",
|
|
118
|
+
"project_name": project_name,
|
|
119
|
+
}
|