gatewaysdk 0.3.2__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.
- gatewaysdk/__init__.py +540 -0
- gatewaysdk/adapter/__init__.py +15 -0
- gatewaysdk/adapter/base.py +94 -0
- gatewaysdk/adapter/messages.py +270 -0
- gatewaysdk/adapter/triplet.py +1026 -0
- gatewaysdk/algorithm/__init__.py +39 -0
- gatewaysdk/algorithm/apo/__init__.py +5 -0
- gatewaysdk/algorithm/apo/apo.py +898 -0
- gatewaysdk/algorithm/apo/prompts/apply_edit_variant01.poml +22 -0
- gatewaysdk/algorithm/apo/prompts/apply_edit_variant02.poml +18 -0
- gatewaysdk/algorithm/apo/prompts/text_gradient_variant01.poml +18 -0
- gatewaysdk/algorithm/apo/prompts/text_gradient_variant02.poml +16 -0
- gatewaysdk/algorithm/apo/prompts/text_gradient_variant03.poml +107 -0
- gatewaysdk/algorithm/base.py +262 -0
- gatewaysdk/algorithm/decorator.py +264 -0
- gatewaysdk/algorithm/evals/__init__.py +7 -0
- gatewaysdk/algorithm/evals/evals.py +217 -0
- gatewaysdk/algorithm/fast.py +250 -0
- gatewaysdk/algorithm/gepa/__init__.py +61 -0
- gatewaysdk/algorithm/gepa/adapter.py +495 -0
- gatewaysdk/algorithm/gepa/gepa.py +570 -0
- gatewaysdk/algorithm/gepa/lib/__init__.py +18 -0
- gatewaysdk/algorithm/gepa/lib/adapters/README.md +12 -0
- gatewaysdk/algorithm/gepa/lib/adapters/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/README.md +341 -0
- gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/__init__.py +1 -0
- gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
- gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/requirements.txt +1 -0
- gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/README.md +0 -0
- gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/default_adapter.py +209 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/README.md +7 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/dspy_adapter.py +307 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/README.md +99 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
- gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/full_program_adapter.py +268 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/GEPA_RAG.md +621 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/__init__.py +56 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
- gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
- gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/README.md +552 -0
- gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/__init__.py +37 -0
- gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_adapter.py +699 -0
- gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_client.py +364 -0
- gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/README.md +9 -0
- gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
- gatewaysdk/algorithm/gepa/lib/api.py +382 -0
- gatewaysdk/algorithm/gepa/lib/core/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/core/adapter.py +180 -0
- gatewaysdk/algorithm/gepa/lib/core/data_loader.py +74 -0
- gatewaysdk/algorithm/gepa/lib/core/engine.py +379 -0
- gatewaysdk/algorithm/gepa/lib/core/result.py +233 -0
- gatewaysdk/algorithm/gepa/lib/core/state.py +636 -0
- gatewaysdk/algorithm/gepa/lib/examples/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/examples/aime.py +24 -0
- gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/eval_default.py +111 -0
- gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/instruction_prompt.txt +9 -0
- gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/optimal_prompt.txt +24 -0
- gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/train_anymaths.py +177 -0
- gatewaysdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/arc_agi.ipynb +25705 -0
- gatewaysdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/example.ipynb +348 -0
- gatewaysdk/algorithm/gepa/lib/examples/mcp_adapter/__init__.py +4 -0
- gatewaysdk/algorithm/gepa/lib/examples/mcp_adapter/mcp_optimization_example.py +456 -0
- gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/RAG_GUIDE.md +613 -0
- gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/__init__.py +9 -0
- gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/rag_optimization.py +820 -0
- gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/requirements-rag.txt +29 -0
- gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/instruction_prompt.txt +16 -0
- gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/terminus.txt +9 -0
- gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/train_terminus.py +161 -0
- gatewaysdk/algorithm/gepa/lib/gepa_utils.py +117 -0
- gatewaysdk/algorithm/gepa/lib/logging/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/logging/experiment_tracker.py +187 -0
- gatewaysdk/algorithm/gepa/lib/logging/logger.py +75 -0
- gatewaysdk/algorithm/gepa/lib/logging/utils.py +103 -0
- gatewaysdk/algorithm/gepa/lib/proposer/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/proposer/base.py +31 -0
- gatewaysdk/algorithm/gepa/lib/proposer/merge.py +357 -0
- gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/base.py +49 -0
- gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/reflective_mutation.py +176 -0
- gatewaysdk/algorithm/gepa/lib/py.typed +0 -0
- gatewaysdk/algorithm/gepa/lib/strategies/__init__.py +0 -0
- gatewaysdk/algorithm/gepa/lib/strategies/batch_sampler.py +77 -0
- gatewaysdk/algorithm/gepa/lib/strategies/candidate_selector.py +50 -0
- gatewaysdk/algorithm/gepa/lib/strategies/component_selector.py +36 -0
- gatewaysdk/algorithm/gepa/lib/strategies/eval_policy.py +64 -0
- gatewaysdk/algorithm/gepa/lib/strategies/instruction_proposal.py +126 -0
- gatewaysdk/algorithm/gepa/lib/utils/__init__.py +10 -0
- gatewaysdk/algorithm/gepa/lib/utils/stop_condition.py +196 -0
- gatewaysdk/algorithm/gepa/tracing.py +105 -0
- gatewaysdk/algorithm/utils.py +177 -0
- gatewaysdk/algorithm/verl/__init__.py +5 -0
- gatewaysdk/algorithm/verl/interface.py +202 -0
- gatewaysdk/automations.py +111 -0
- gatewaysdk/benchmark_hub/README.md +88 -0
- gatewaysdk/benchmark_hub/__init__.py +119 -0
- gatewaysdk/benchmark_hub/_archive.py +189 -0
- gatewaysdk/benchmark_hub/_tar.py +6 -0
- gatewaysdk/benchmark_hub/client.py +992 -0
- gatewaysdk/benchmark_hub/dispatch_shard.py +34 -0
- gatewaysdk/benchmark_hub/eval_config.py +20 -0
- gatewaysdk/benchmark_hub/evals.py +351 -0
- gatewaysdk/benchmark_hub/harbor_adapter.py +673 -0
- gatewaysdk/benchmark_hub/path_utils.py +81 -0
- gatewaysdk/benchmark_hub/save_utils.py +101 -0
- gatewaysdk/benchmark_hub/verifiers_adapter.py +327 -0
- gatewaysdk/benchmark_hub/versioning.py +72 -0
- gatewaysdk/build.py +515 -0
- gatewaysdk/cli/__init__.py +58 -0
- gatewaysdk/cli/agent_runner.py +132 -0
- gatewaysdk/cli/http_client.py +115 -0
- gatewaysdk/cli/platform.py +4086 -0
- gatewaysdk/cli/prometheus.py +115 -0
- gatewaysdk/cli/release_gate.py +215 -0
- gatewaysdk/cli/store.py +131 -0
- gatewaysdk/cli/vllm.py +29 -0
- gatewaysdk/client.py +406 -0
- gatewaysdk/config.py +348 -0
- gatewaysdk/connectors/__init__.py +25 -0
- gatewaysdk/connectors/client.py +203 -0
- gatewaysdk/connectors/skill.py +25 -0
- gatewaysdk/connectors/template.py +106 -0
- gatewaysdk/context.py +606 -0
- gatewaysdk/emitter/__init__.py +43 -0
- gatewaysdk/emitter/annotation.py +370 -0
- gatewaysdk/emitter/exception.py +54 -0
- gatewaysdk/emitter/message.py +61 -0
- gatewaysdk/emitter/object.py +117 -0
- gatewaysdk/emitter/reward.py +320 -0
- gatewaysdk/env_var.py +156 -0
- gatewaysdk/environment/__init__.py +108 -0
- gatewaysdk/environment/_bundle.py +281 -0
- gatewaysdk/environment/_harbor.py +276 -0
- gatewaysdk/environment/_materialize.py +97 -0
- gatewaysdk/environment/_tar.py +33 -0
- gatewaysdk/environment/_world.py +346 -0
- gatewaysdk/environment/core.py +527 -0
- gatewaysdk/environment/errors.py +58 -0
- gatewaysdk/environment/runtime.py +150 -0
- gatewaysdk/environment/schema/__init__.py +35 -0
- gatewaysdk/environment/schema/__main__.py +225 -0
- gatewaysdk/environment/schema/_slack_fidelity.py +116 -0
- gatewaysdk/environment/schema/_slack_scenario.py +183 -0
- gatewaysdk/environment/schema/api.py +1628 -0
- gatewaysdk/environment/schema/batch.py +755 -0
- gatewaysdk/environment/schema/compiler.py +615 -0
- gatewaysdk/environment/schema/conform.py +428 -0
- gatewaysdk/environment/schema/connector.py +777 -0
- gatewaysdk/environment/schema/connectors/apple-business-manager/handlers.py +17 -0
- gatewaysdk/environment/schema/connectors/apple-business-manager/parity.json +28 -0
- gatewaysdk/environment/schema/connectors/apple-business-manager/provenance.json +233 -0
- gatewaysdk/environment/schema/connectors/apple-business-manager/scope.toml +112 -0
- gatewaysdk/environment/schema/connectors/apple-business-manager/world.json +1380 -0
- gatewaysdk/environment/schema/connectors/base.json +28 -0
- gatewaysdk/environment/schema/connectors/custom/handlers.py +18 -0
- gatewaysdk/environment/schema/connectors/custom/world.json +123 -0
- gatewaysdk/environment/schema/connectors/github/handlers.py +81 -0
- gatewaysdk/environment/schema/connectors/github/parity.json +37 -0
- gatewaysdk/environment/schema/connectors/github/provenance.json +134 -0
- gatewaysdk/environment/schema/connectors/github/scope.toml +158 -0
- gatewaysdk/environment/schema/connectors/github/world.json +5869 -0
- gatewaysdk/environment/schema/connectors/google-calendar/handlers.py +23 -0
- gatewaysdk/environment/schema/connectors/google-calendar/world.json +209 -0
- gatewaysdk/environment/schema/connectors/google-drive/handlers.py +18 -0
- gatewaysdk/environment/schema/connectors/google-drive/world.json +286 -0
- gatewaysdk/environment/schema/connectors/jamf/handlers.py +34 -0
- gatewaysdk/environment/schema/connectors/jamf/parity.json +49 -0
- gatewaysdk/environment/schema/connectors/jamf/provenance.json +181 -0
- gatewaysdk/environment/schema/connectors/jamf/scope.toml +128 -0
- gatewaysdk/environment/schema/connectors/jamf/world.json +2218 -0
- gatewaysdk/environment/schema/connectors/jira/handlers.py +28 -0
- gatewaysdk/environment/schema/connectors/jira/world.json +389 -0
- gatewaysdk/environment/schema/connectors/kandji/handlers.py +38 -0
- gatewaysdk/environment/schema/connectors/kandji/parity.json +29 -0
- gatewaysdk/environment/schema/connectors/kandji/provenance.json +152 -0
- gatewaysdk/environment/schema/connectors/kandji/scope.toml +120 -0
- gatewaysdk/environment/schema/connectors/kandji/world.json +765 -0
- gatewaysdk/environment/schema/connectors/linear/handlers.py +20 -0
- gatewaysdk/environment/schema/connectors/linear/world.json +363 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/fidelity.json +45 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/handlers.py +1 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/provenance.json +851 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/scope.md +231 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/teams-types.json +1710 -0
- gatewaysdk/environment/schema/connectors/microsoft-teams/world.json +858 -0
- gatewaysdk/environment/schema/connectors/netsuite/handlers.py +32 -0
- gatewaysdk/environment/schema/connectors/netsuite/parity.json +50 -0
- gatewaysdk/environment/schema/connectors/netsuite/provenance.json +137 -0
- gatewaysdk/environment/schema/connectors/netsuite/scope.toml +78 -0
- gatewaysdk/environment/schema/connectors/netsuite/world.json +16243 -0
- gatewaysdk/environment/schema/connectors/salesforce/handlers.py +806 -0
- gatewaysdk/environment/schema/connectors/salesforce/parity.json +46 -0
- gatewaysdk/environment/schema/connectors/salesforce/provenance.json +143 -0
- gatewaysdk/environment/schema/connectors/salesforce/scope.toml +174 -0
- gatewaysdk/environment/schema/connectors/salesforce/world.json +4695 -0
- gatewaysdk/environment/schema/connectors/slack/MODEL.md +236 -0
- gatewaysdk/environment/schema/connectors/slack/capabilities.json +1039 -0
- gatewaysdk/environment/schema/connectors/slack/handlers.py +762 -0
- gatewaysdk/environment/schema/connectors/slack/parity.json +65 -0
- gatewaysdk/environment/schema/connectors/slack/provenance.json +294 -0
- gatewaysdk/environment/schema/connectors/slack/scope.toml +44 -0
- gatewaysdk/environment/schema/connectors/slack/world.json +4993 -0
- gatewaysdk/environment/schema/connectors/workday/handlers.py +75 -0
- gatewaysdk/environment/schema/connectors/workday/parity.json +36 -0
- gatewaysdk/environment/schema/connectors/workday/provenance.json +179 -0
- gatewaysdk/environment/schema/connectors/workday/scope.toml +180 -0
- gatewaysdk/environment/schema/connectors/workday/world.json +806 -0
- gatewaysdk/environment/schema/data.py +593 -0
- gatewaysdk/environment/schema/extract.py +263 -0
- gatewaysdk/environment/schema/host.py +2437 -0
- gatewaysdk/environment/schema/host_surfaces.py +275 -0
- gatewaysdk/environment/schema/platform.py +1272 -0
- gatewaysdk/environment/schema/scaffold.py +250 -0
- gatewaysdk/environment/schema/skill.py +118 -0
- gatewaysdk/environment/schema/skills/agents/connector-template-author.md +202 -0
- gatewaysdk/environment/schema/skills/connector-schema-authoring/CONNECTORS.md +331 -0
- gatewaysdk/environment/schema/skills/connector-schema-authoring/CONVENTIONS.md +100 -0
- gatewaysdk/environment/schema/skills/connector-schema-authoring/SKILL.md +313 -0
- gatewaysdk/environment/schema/skills/world-data-ingestion/ROWS.md +223 -0
- gatewaysdk/environment/schema/skills/world-data-ingestion/SKILL.md +293 -0
- gatewaysdk/environment/schema/skills/worlds-getting-started/SKILL.md +390 -0
- gatewaysdk/environment/schema/slack.py +757 -0
- gatewaysdk/environment/schema/snapshot.py +479 -0
- gatewaysdk/environment/schema/store.py +904 -0
- gatewaysdk/environment/schema/tasks.py +525 -0
- gatewaysdk/environment/schema/tools_world.py +322 -0
- gatewaysdk/environment/schema/validation.py +577 -0
- gatewaysdk/environment/schema/worker.py +78 -0
- gatewaysdk/environment/schema/workers.py +500 -0
- gatewaysdk/environment/schema/world_tests.py +336 -0
- gatewaysdk/environments/__init__.py +24 -0
- gatewaysdk/environments/client.py +486 -0
- gatewaysdk/environments/types.py +318 -0
- gatewaysdk/execution/__init__.py +15 -0
- gatewaysdk/execution/base.py +64 -0
- gatewaysdk/execution/client_server.py +443 -0
- gatewaysdk/execution/events.py +69 -0
- gatewaysdk/execution/inter_process.py +16 -0
- gatewaysdk/execution/shared_memory.py +282 -0
- gatewaysdk/experiments/__init__.py +90 -0
- gatewaysdk/experiments/assignment.py +177 -0
- gatewaysdk/experiments/client.py +877 -0
- gatewaysdk/experiments/exposure.py +222 -0
- gatewaysdk/experiments/types.py +81 -0
- gatewaysdk/importers/__init__.py +49 -0
- gatewaysdk/importers/_normalize.py +91 -0
- gatewaysdk/importers/builder.py +173 -0
- gatewaysdk/importers/client.py +95 -0
- gatewaysdk/importers/langsmith.py +196 -0
- gatewaysdk/importers/recipes.py +249 -0
- gatewaysdk/instrumentation/__init__.py +300 -0
- gatewaysdk/instrumentation/agentops.py +314 -0
- gatewaysdk/instrumentation/agentops_langchain.py +45 -0
- gatewaysdk/instrumentation/base.py +119 -0
- gatewaysdk/instrumentation/litellm.py +83 -0
- gatewaysdk/instrumentation/registry.py +273 -0
- gatewaysdk/instrumentation/vllm.py +81 -0
- gatewaysdk/instrumentation/weave.py +500 -0
- gatewaysdk/integrations/__init__.py +15 -0
- gatewaysdk/integrations/gateway/__init__.py +11 -0
- gatewaysdk/integrations/gateway/client.py +171 -0
- gatewaysdk/integrations/tool_access.py +549 -0
- gatewaysdk/litagent/__init__.py +11 -0
- gatewaysdk/litagent/decorator.py +536 -0
- gatewaysdk/litagent/litagent.py +252 -0
- gatewaysdk/llm_proxy.py +1742 -0
- gatewaysdk/logging.py +370 -0
- gatewaysdk/memory.py +278 -0
- gatewaysdk/personas.py +186 -0
- gatewaysdk/platform/__init__.py +17 -0
- gatewaysdk/platform/builder.py +221 -0
- gatewaysdk/platform/compatibility.py +67 -0
- gatewaysdk/platform/manifest.py +185 -0
- gatewaysdk/platform/orchestrator.py +901 -0
- gatewaysdk/platform/registry.py +122 -0
- gatewaysdk/platform/worker.py +864 -0
- gatewaysdk/replay/__init__.py +1032 -0
- gatewaysdk/replay/pytest.py +56 -0
- gatewaysdk/reward.py +7 -0
- gatewaysdk/run.py +1781 -0
- gatewaysdk/runner/__init__.py +11 -0
- gatewaysdk/runner/agent.py +878 -0
- gatewaysdk/runner/base.py +182 -0
- gatewaysdk/runner/legacy.py +309 -0
- gatewaysdk/security.py +700 -0
- gatewaysdk/semconv.py +170 -0
- gatewaysdk/server.py +399 -0
- gatewaysdk/sessions.py +282 -0
- gatewaysdk/store/__init__.py +45 -0
- gatewaysdk/store/base.py +908 -0
- gatewaysdk/store/client_server.py +2093 -0
- gatewaysdk/store/collection/__init__.py +30 -0
- gatewaysdk/store/collection/base.py +587 -0
- gatewaysdk/store/collection/memory.py +970 -0
- gatewaysdk/store/collection/mongo.py +1412 -0
- gatewaysdk/store/collection_based.py +1823 -0
- gatewaysdk/store/gateway.py +983 -0
- gatewaysdk/store/gateway_listener.py +465 -0
- gatewaysdk/store/listener.py +58 -0
- gatewaysdk/store/memory.py +396 -0
- gatewaysdk/store/mongo.py +165 -0
- gatewaysdk/store/redis_stream.py +517 -0
- gatewaysdk/store/sqlite.py +3 -0
- gatewaysdk/store/threading.py +370 -0
- gatewaysdk/store/utils.py +142 -0
- gatewaysdk/tracer/__init__.py +14 -0
- gatewaysdk/tracer/base.py +286 -0
- gatewaysdk/tracer/dummy.py +106 -0
- gatewaysdk/tracer/otel.py +559 -0
- gatewaysdk/tracing/__init__.py +110 -0
- gatewaysdk/tracing/api.py +808 -0
- gatewaysdk/tracing/attributes.py +9 -0
- gatewaysdk/tracing/context.py +272 -0
- gatewaysdk/tracing/exporters/__init__.py +10 -0
- gatewaysdk/tracing/exporters/gateway.py +228 -0
- gatewaysdk/tracing/identity.py +288 -0
- gatewaysdk/tracing/init.py +620 -0
- gatewaysdk/tracing/instrumentors/__init__.py +15 -0
- gatewaysdk/tracing/instrumentors/claude_agent_sdk.py +766 -0
- gatewaysdk/tracing/instrumentors/instrumentation_principles.md +294 -0
- gatewaysdk/tracing/instrumentors/registry.py +352 -0
- gatewaysdk/tracing/mapping.py +729 -0
- gatewaysdk/tracing/processors.py +393 -0
- gatewaysdk/tracing/push.py +617 -0
- gatewaysdk/tracing/push_models.py +247 -0
- gatewaysdk/tracing/semconv.py +294 -0
- gatewaysdk/tracing/span_builder.py +356 -0
- gatewaysdk/trainer/__init__.py +6 -0
- gatewaysdk/trainer/init_utils.py +263 -0
- gatewaysdk/trainer/legacy.py +359 -0
- gatewaysdk/trainer/registry.py +12 -0
- gatewaysdk/trainer/trainer.py +638 -0
- gatewaysdk/types/__init__.py +63 -0
- gatewaysdk/types/core.py +556 -0
- gatewaysdk/types/resources.py +204 -0
- gatewaysdk/types/tracer.py +515 -0
- gatewaysdk/types/tracing.py +162 -0
- gatewaysdk/users.py +251 -0
- gatewaysdk/utils/__init__.py +1 -0
- gatewaysdk/utils/id.py +18 -0
- gatewaysdk/utils/metrics.py +1025 -0
- gatewaysdk/utils/otel.py +550 -0
- gatewaysdk/utils/otlp.py +556 -0
- gatewaysdk/utils/redact.py +22 -0
- gatewaysdk/utils/server_launcher.py +1045 -0
- gatewaysdk/utils/system_snapshot.py +90 -0
- gatewaysdk/verl/__init__.py +8 -0
- gatewaysdk/verl/__main__.py +6 -0
- gatewaysdk/verl/async_server.py +46 -0
- gatewaysdk/verl/config.yaml +27 -0
- gatewaysdk/verl/daemon.py +1154 -0
- gatewaysdk/verl/dataset.py +44 -0
- gatewaysdk/verl/entrypoint.py +248 -0
- gatewaysdk/verl/trainer.py +549 -0
- gatewaysdk/world_browser.py +748 -0
- gatewaysdk/world_data.py +453 -0
- gatewaysdk/world_sessions.py +978 -0
- gatewaysdk/world_tasks.py +285 -0
- gatewaysdk/world_tools.py +115 -0
- gatewaysdk/worlds.py +639 -0
- gatewaysdk-0.3.2.dist-info/METADATA +236 -0
- gatewaysdk-0.3.2.dist-info/RECORD +376 -0
- gatewaysdk-0.3.2.dist-info/WHEEL +4 -0
- gatewaysdk-0.3.2.dist-info/entry_points.txt +5 -0
- gatewaysdk-0.3.2.dist-info/licenses/LICENSE +19 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
import threading
|
|
8
|
+
import warnings
|
|
9
|
+
from contextlib import asynccontextmanager, contextmanager
|
|
10
|
+
from typing import Any, AsyncGenerator, Awaitable, Iterator, List, Optional
|
|
11
|
+
|
|
12
|
+
import opentelemetry.trace as trace_api
|
|
13
|
+
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor
|
|
14
|
+
from opentelemetry.sdk.trace import TracerProvider as TracerProviderImpl
|
|
15
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
|
|
16
|
+
|
|
17
|
+
from gatewaysdk.store.base import LightningStore
|
|
18
|
+
from gatewaysdk.types import Attributes, Span, SpanCoreFields, SpanRecordingContext, StatusCode, TraceStatus
|
|
19
|
+
from gatewaysdk.types.tracer import convert_timestamp
|
|
20
|
+
from gatewaysdk.utils.otlp import LightningStoreOTLPExporter
|
|
21
|
+
|
|
22
|
+
from .base import Tracer, with_active_tracer_context
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
STORE_WRITE_TIMEOUT_SECONDS = 10.0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def to_otel_status_code(status_code: StatusCode) -> trace_api.StatusCode:
|
|
30
|
+
if status_code == "UNSET":
|
|
31
|
+
return trace_api.StatusCode.UNSET
|
|
32
|
+
elif status_code == "ERROR":
|
|
33
|
+
return trace_api.StatusCode.ERROR
|
|
34
|
+
else:
|
|
35
|
+
return trace_api.StatusCode.OK
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class OtelSpanRecordingContext(SpanRecordingContext):
|
|
39
|
+
def __init__(self, span: trace_api.Span) -> None:
|
|
40
|
+
self._span = span
|
|
41
|
+
|
|
42
|
+
def record_exception(self, exception: BaseException) -> None:
|
|
43
|
+
self._span.record_exception(exception)
|
|
44
|
+
self.record_status("ERROR", str(exception))
|
|
45
|
+
|
|
46
|
+
def record_attributes(self, attributes: Attributes) -> None:
|
|
47
|
+
self._span.set_attributes(attributes)
|
|
48
|
+
|
|
49
|
+
def record_status(self, status_code: StatusCode, description: Optional[str] = None) -> None:
|
|
50
|
+
otel_status_code = to_otel_status_code(status_code)
|
|
51
|
+
self._span.set_status(otel_status_code, description)
|
|
52
|
+
|
|
53
|
+
def get_otel_span(self) -> trace_api.Span:
|
|
54
|
+
return self._span
|
|
55
|
+
|
|
56
|
+
def get_recorded_span(self) -> SpanCoreFields:
|
|
57
|
+
if isinstance(self._span, ReadableSpan):
|
|
58
|
+
return SpanCoreFields(
|
|
59
|
+
name=self._span.name,
|
|
60
|
+
attributes=dict(self._span.attributes) if self._span.attributes else {},
|
|
61
|
+
start_time=convert_timestamp(self._span.start_time),
|
|
62
|
+
end_time=convert_timestamp(self._span.end_time),
|
|
63
|
+
status=TraceStatus.from_opentelemetry(self._span.status),
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError(f"Span is not a ReadableSpan: {self._span}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class OtelTracer(Tracer):
|
|
70
|
+
"""Tracer that provides a basic OpenTelemetry tracer provider.
|
|
71
|
+
|
|
72
|
+
You should be able to collect gatewaysdk signals like rewards with this tracer,
|
|
73
|
+
but no other function instrumentations like `openai.chat.completion`.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(self):
|
|
77
|
+
super().__init__()
|
|
78
|
+
# This provider is only initialized when the worker is initialized.
|
|
79
|
+
self._tracer_provider: Optional[trace_api.TracerProvider] = None
|
|
80
|
+
self._lightning_span_processor: Optional[LightningSpanProcessor] = None
|
|
81
|
+
self._simple_span_processor: Optional[SimpleSpanProcessor] = None
|
|
82
|
+
self._otlp_span_exporter: Optional[LightningStoreOTLPExporter] = None
|
|
83
|
+
self._initialized: bool = False
|
|
84
|
+
|
|
85
|
+
def init_worker(self, worker_id: int, store: Optional[LightningStore] = None):
|
|
86
|
+
super().init_worker(worker_id, store)
|
|
87
|
+
self._initialize_tracer_provider(worker_id)
|
|
88
|
+
|
|
89
|
+
def _initialize_tracer_provider(self, worker_id: int):
|
|
90
|
+
logger.info(f"[Worker {worker_id}] Setting up OpenTelemetry tracer...")
|
|
91
|
+
|
|
92
|
+
if self._initialized:
|
|
93
|
+
logger.info(f"[Worker {worker_id}] Tracer provider is already initialized. Skipping initialization.")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
# Delegate provider creation to tracing.init() — this ensures:
|
|
97
|
+
# 1. A single global TracerProvider exists
|
|
98
|
+
# 2. TracingContextSpanProcessor is registered (auto-injects run_id,
|
|
99
|
+
# session_id, call_type, tags, environment into ALL spans)
|
|
100
|
+
# We pass exporters=[] to suppress Gateway OTLP auto-detection since
|
|
101
|
+
# we manage our own LightningStoreOTLPExporter below.
|
|
102
|
+
from gatewaysdk.tracing.init import init as tracing_init
|
|
103
|
+
|
|
104
|
+
tracing_init(
|
|
105
|
+
service_name="gatewaysdk-trainer",
|
|
106
|
+
exporters=[],
|
|
107
|
+
instrument_default=False,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Retrieve the global provider (created or reused by tracing.init)
|
|
111
|
+
provider = trace_api.get_tracer_provider()
|
|
112
|
+
if not isinstance(provider, TracerProviderImpl):
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
f"Expected TracerProviderImpl after tracing.init(), got {type(provider)}. "
|
|
115
|
+
"Ensure no other library replaces the global TracerProvider."
|
|
116
|
+
)
|
|
117
|
+
self._tracer_provider = provider
|
|
118
|
+
|
|
119
|
+
# Add LightningSpanProcessor for local span recording + store submission
|
|
120
|
+
self._lightning_span_processor = LightningSpanProcessor()
|
|
121
|
+
self._tracer_provider.add_span_processor(self._lightning_span_processor)
|
|
122
|
+
|
|
123
|
+
# Add LightningStoreOTLPExporter for OTLP export to Gateway
|
|
124
|
+
self._otlp_span_exporter = LightningStoreOTLPExporter()
|
|
125
|
+
self._simple_span_processor = SimpleSpanProcessor(self._otlp_span_exporter)
|
|
126
|
+
self._tracer_provider.add_span_processor(self._simple_span_processor)
|
|
127
|
+
|
|
128
|
+
self._initialized = True
|
|
129
|
+
logger.info(f"[Worker {worker_id}] OpenTelemetry tracer provider initialized (via tracing.init).")
|
|
130
|
+
|
|
131
|
+
def teardown_worker(self, worker_id: int):
|
|
132
|
+
super().teardown_worker(worker_id)
|
|
133
|
+
logger.info(f"[Worker {worker_id}] Tearing down OpenTelemetry tracer does NOT remove the tracer provider.")
|
|
134
|
+
|
|
135
|
+
@with_active_tracer_context
|
|
136
|
+
@asynccontextmanager
|
|
137
|
+
async def trace_context(
|
|
138
|
+
self,
|
|
139
|
+
name: Optional[str] = None,
|
|
140
|
+
*,
|
|
141
|
+
store: Optional[LightningStore] = None,
|
|
142
|
+
rollout_id: Optional[str] = None,
|
|
143
|
+
attempt_id: Optional[str] = None,
|
|
144
|
+
) -> AsyncGenerator[trace_api.Tracer, None]:
|
|
145
|
+
"""
|
|
146
|
+
Starts a new tracing context. This should be used as a context manager.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
name: Optional name for the tracing context.
|
|
150
|
+
store: Optional store to add the spans to.
|
|
151
|
+
rollout_id: Optional rollout ID to add the spans to.
|
|
152
|
+
attempt_id: Optional attempt ID to add the spans to.
|
|
153
|
+
|
|
154
|
+
Yields:
|
|
155
|
+
The OpenTelemetry tracer instance to collect spans.
|
|
156
|
+
"""
|
|
157
|
+
if not self._lightning_span_processor:
|
|
158
|
+
raise RuntimeError("LightningSpanProcessor is not initialized. Call init_worker() first.")
|
|
159
|
+
|
|
160
|
+
if store is not None:
|
|
161
|
+
warnings.warn(
|
|
162
|
+
"store is deprecated in favor of init_worker(). It will be removed in the future.",
|
|
163
|
+
DeprecationWarning,
|
|
164
|
+
stacklevel=3,
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
store = self._store
|
|
168
|
+
|
|
169
|
+
if rollout_id is not None and attempt_id is not None:
|
|
170
|
+
if store is None:
|
|
171
|
+
raise ValueError("store is required to be initialized when rollout_id and attempt_id are provided")
|
|
172
|
+
if store.capabilities.get("otlp_traces", False) is True:
|
|
173
|
+
logger.debug(f"Tracing to LightningStore rollout_id={rollout_id}, attempt_id={attempt_id}")
|
|
174
|
+
await self._enable_native_otlp_exporter(store, rollout_id, attempt_id)
|
|
175
|
+
else:
|
|
176
|
+
self._disable_native_otlp_exporter()
|
|
177
|
+
ctx = self._lightning_span_processor.with_context(store=store, rollout_id=rollout_id, attempt_id=attempt_id)
|
|
178
|
+
with ctx:
|
|
179
|
+
yield trace_api.get_tracer(__name__, tracer_provider=self._tracer_provider)
|
|
180
|
+
elif rollout_id is None and attempt_id is None:
|
|
181
|
+
self._disable_native_otlp_exporter()
|
|
182
|
+
with self._lightning_span_processor:
|
|
183
|
+
yield trace_api.get_tracer(__name__, tracer_provider=self._tracer_provider)
|
|
184
|
+
else:
|
|
185
|
+
raise ValueError("rollout_id and attempt_id must be either all provided or all None")
|
|
186
|
+
|
|
187
|
+
def create_span(
|
|
188
|
+
self,
|
|
189
|
+
name: str,
|
|
190
|
+
attributes: Optional[Attributes] = None,
|
|
191
|
+
timestamp: Optional[float] = None,
|
|
192
|
+
status: Optional[TraceStatus] = None,
|
|
193
|
+
) -> SpanCoreFields:
|
|
194
|
+
# Fire the span to the current active tracer provider.
|
|
195
|
+
tracer_provider = self._get_tracer_provider()
|
|
196
|
+
tracer = tracer_provider.get_tracer(__name__)
|
|
197
|
+
span = tracer.start_span(
|
|
198
|
+
name, attributes=attributes, start_time=int(timestamp * 1_000_000_000) if timestamp else None
|
|
199
|
+
)
|
|
200
|
+
if status is not None:
|
|
201
|
+
span.set_status(to_otel_status_code(status.status_code), status.description)
|
|
202
|
+
span.end(int(timestamp * 1_000_000_000) if timestamp else None)
|
|
203
|
+
|
|
204
|
+
# The span should have been auto-created by now.
|
|
205
|
+
# Return the core fields of the span.
|
|
206
|
+
if isinstance(span, ReadableSpan):
|
|
207
|
+
return SpanCoreFields(
|
|
208
|
+
name=name,
|
|
209
|
+
attributes=dict(span.attributes) if span.attributes else {},
|
|
210
|
+
start_time=convert_timestamp(span.start_time),
|
|
211
|
+
end_time=convert_timestamp(span.end_time),
|
|
212
|
+
status=TraceStatus.from_opentelemetry(span.status),
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
raise ValueError(f"Span is not a ReadableSpan: {span}")
|
|
216
|
+
|
|
217
|
+
@contextmanager
|
|
218
|
+
def operation_context(
|
|
219
|
+
self,
|
|
220
|
+
name: str,
|
|
221
|
+
attributes: Optional[Attributes] = None,
|
|
222
|
+
start_time: Optional[float] = None,
|
|
223
|
+
end_time: Optional[float] = None,
|
|
224
|
+
) -> Iterator[SpanRecordingContext]:
|
|
225
|
+
if end_time is not None:
|
|
226
|
+
logger.warning("OpenTelemetry doesn't support customizing the end time of a span. End time is ignored.")
|
|
227
|
+
# Record the span to the current active tracer provider.
|
|
228
|
+
tracer_provider = self._get_tracer_provider()
|
|
229
|
+
tracer = tracer_provider.get_tracer(__name__)
|
|
230
|
+
|
|
231
|
+
# Activate the span as the current span within otel.
|
|
232
|
+
with tracer.start_as_current_span(
|
|
233
|
+
name, attributes=attributes, start_time=int(start_time * 1_000_000_000) if start_time else None
|
|
234
|
+
) as span:
|
|
235
|
+
recording_context = OtelSpanRecordingContext(span)
|
|
236
|
+
try:
|
|
237
|
+
yield recording_context
|
|
238
|
+
except Exception as exc:
|
|
239
|
+
recording_context.record_exception(exc)
|
|
240
|
+
raise
|
|
241
|
+
|
|
242
|
+
# No need to retrieve the span here. It's already been sent to otel processor.
|
|
243
|
+
|
|
244
|
+
def get_last_trace(self) -> List[Span]:
|
|
245
|
+
"""
|
|
246
|
+
Retrieves the raw list of captured spans from the most recent trace.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
A list of [`Span`][gatewaysdk.Span] objects captured during the most recent trace.
|
|
250
|
+
"""
|
|
251
|
+
if not self._lightning_span_processor:
|
|
252
|
+
raise RuntimeError("LightningSpanProcessor is not initialized. Call init_worker() first.")
|
|
253
|
+
return self._lightning_span_processor.spans()
|
|
254
|
+
|
|
255
|
+
def _get_tracer_provider(self) -> TracerProviderImpl:
|
|
256
|
+
if self._tracer_provider is None:
|
|
257
|
+
raise RuntimeError("TracerProvider is not initialized. Call init_worker() first.")
|
|
258
|
+
if not isinstance(self._tracer_provider, TracerProviderImpl):
|
|
259
|
+
raise TypeError(f"TracerProvider is not a opentelemetry.sdk.trace.TracerProvider: {self._tracer_provider}")
|
|
260
|
+
return self._tracer_provider
|
|
261
|
+
|
|
262
|
+
async def _enable_native_otlp_exporter(self, store: LightningStore, rollout_id: str, attempt_id: str):
|
|
263
|
+
tracer_provider = self._get_tracer_provider()
|
|
264
|
+
active_span_processor = tracer_provider._active_span_processor # pyright: ignore[reportPrivateUsage]
|
|
265
|
+
|
|
266
|
+
# Note: We do NOT mutate tracer_provider._resource here. The
|
|
267
|
+
# LightningStoreOTLPExporter uses SpanWithResource wrappers to enrich
|
|
268
|
+
# spans with rollout_id/attempt_id at export time, without mutating
|
|
269
|
+
# shared ReadableSpan or TracerProvider resources.
|
|
270
|
+
|
|
271
|
+
# Fetch rollout metadata for tracing configuration (environment, tags)
|
|
272
|
+
environment = None
|
|
273
|
+
tags = None
|
|
274
|
+
try:
|
|
275
|
+
rollout = await store.get_rollout_by_id(rollout_id)
|
|
276
|
+
if rollout and rollout.metadata:
|
|
277
|
+
environment = rollout.metadata.get("environment")
|
|
278
|
+
tags = rollout.metadata.get("tags")
|
|
279
|
+
logger.debug(
|
|
280
|
+
f"Fetched tracing metadata for rollout {rollout_id}: environment={environment}, tags={tags}"
|
|
281
|
+
)
|
|
282
|
+
except Exception as e:
|
|
283
|
+
logger.warning(f"Failed to fetch rollout metadata for {rollout_id}: {e}")
|
|
284
|
+
|
|
285
|
+
instrumented = False
|
|
286
|
+
candidates: List[str] = []
|
|
287
|
+
for processor in active_span_processor._span_processors: # pyright: ignore[reportPrivateUsage]
|
|
288
|
+
if isinstance(processor, LightningSpanProcessor):
|
|
289
|
+
# We don't need the LightningSpanProcessor any more.
|
|
290
|
+
logger.debug("LightningSpanProcessor already present in TracerProvider, disabling it.")
|
|
291
|
+
processor.disable_store_submission = True
|
|
292
|
+
elif isinstance(processor, (SimpleSpanProcessor, BatchSpanProcessor)):
|
|
293
|
+
# Instead, we rely on the OTLPSpanExporter to send spans to the store.
|
|
294
|
+
if isinstance(processor.span_exporter, LightningStoreOTLPExporter):
|
|
295
|
+
# Get optional auth headers from store
|
|
296
|
+
otlp_headers = None
|
|
297
|
+
if hasattr(store, "get_otlp_headers"):
|
|
298
|
+
otlp_headers = store.get_otlp_headers()
|
|
299
|
+
processor.span_exporter.enable_store_otlp(
|
|
300
|
+
store.otlp_traces_endpoint(),
|
|
301
|
+
rollout_id,
|
|
302
|
+
attempt_id,
|
|
303
|
+
headers=otlp_headers,
|
|
304
|
+
environment=environment,
|
|
305
|
+
tags=tags,
|
|
306
|
+
)
|
|
307
|
+
logger.debug(f"Set LightningStoreOTLPExporter endpoint to {store.otlp_traces_endpoint()}")
|
|
308
|
+
instrumented = True
|
|
309
|
+
else:
|
|
310
|
+
candidates.append(
|
|
311
|
+
f"{processor.__class__.__name__} with {processor.span_exporter.__class__.__name__}"
|
|
312
|
+
)
|
|
313
|
+
else:
|
|
314
|
+
candidates.append(f"{processor.__class__.__name__}")
|
|
315
|
+
|
|
316
|
+
if not instrumented:
|
|
317
|
+
raise RuntimeError(
|
|
318
|
+
"Failed to enable native OTLP exporter: no BatchSpanProcessor or SimpleSpanProcessor with "
|
|
319
|
+
"LightningStoreOTLPExporter found in TracerProvider. Please try using a non-OTLP store."
|
|
320
|
+
"Candidates are: " + ", ".join(candidates)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def _disable_native_otlp_exporter(self):
|
|
324
|
+
tracer_provider = self._get_tracer_provider()
|
|
325
|
+
active_span_processor = tracer_provider._active_span_processor # pyright: ignore[reportPrivateUsage]
|
|
326
|
+
for processor in active_span_processor._span_processors: # pyright: ignore[reportPrivateUsage]
|
|
327
|
+
if isinstance(processor, LightningSpanProcessor):
|
|
328
|
+
logger.debug("Enabling LightningSpanProcessor in TracerProvider.")
|
|
329
|
+
processor.disable_store_submission = False
|
|
330
|
+
elif isinstance(processor, (SimpleSpanProcessor, BatchSpanProcessor)):
|
|
331
|
+
if isinstance(processor.span_exporter, LightningStoreOTLPExporter):
|
|
332
|
+
processor.span_exporter.disable_store_otlp()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class LightningSpanProcessor(SpanProcessor):
|
|
336
|
+
"""Span processor that subclasses OpenTelemetry's `SpanProcessor` and adds support to dump traces
|
|
337
|
+
to a [`LightningStore`][gatewaysdk.LightningStore].
|
|
338
|
+
|
|
339
|
+
It serves two purposes:
|
|
340
|
+
|
|
341
|
+
1. Records all the spans in a local buffer.
|
|
342
|
+
2. Submits the spans to the event loop to be added to the store.
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def __init__(self, disable_store_submission: bool = False):
|
|
346
|
+
self._disable_store_submission: bool = disable_store_submission
|
|
347
|
+
self._spans: List[Span] = []
|
|
348
|
+
|
|
349
|
+
# Store related context and states
|
|
350
|
+
self._store: Optional[LightningStore] = None
|
|
351
|
+
self._rollout_id: Optional[str] = None
|
|
352
|
+
self._attempt_id: Optional[str] = None
|
|
353
|
+
self._local_sequence_id: int = 0
|
|
354
|
+
self._lock = threading.Lock()
|
|
355
|
+
|
|
356
|
+
# private asyncio loop running in a daemon thread
|
|
357
|
+
self._loop_ready = threading.Event()
|
|
358
|
+
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
|
359
|
+
self._loop_thread: Optional[threading.Thread] = None
|
|
360
|
+
self._loop_init_lock = threading.Lock()
|
|
361
|
+
|
|
362
|
+
def __repr__(self) -> str:
|
|
363
|
+
return (
|
|
364
|
+
f"{self.__class__.__name__}("
|
|
365
|
+
+ f"disable_store_submission={self.disable_store_submission}, "
|
|
366
|
+
+ f"store={self.store!r}, "
|
|
367
|
+
+ f"rollout_id={self.rollout_id!r}, "
|
|
368
|
+
+ f"attempt_id={self.attempt_id!r})"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def store(self) -> Optional[LightningStore]:
|
|
373
|
+
"""The store to submit the spans to."""
|
|
374
|
+
return self._store
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def rollout_id(self) -> Optional[str]:
|
|
378
|
+
"""The rollout ID to submit the spans to."""
|
|
379
|
+
return self._rollout_id
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def attempt_id(self) -> Optional[str]:
|
|
383
|
+
"""The attempt ID to submit the spans to."""
|
|
384
|
+
return self._attempt_id
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def disable_store_submission(self) -> bool:
|
|
388
|
+
"""Whether to disable submitting spans to the store."""
|
|
389
|
+
return self._disable_store_submission
|
|
390
|
+
|
|
391
|
+
@disable_store_submission.setter
|
|
392
|
+
def disable_store_submission(self, value: bool) -> None:
|
|
393
|
+
self._disable_store_submission = value
|
|
394
|
+
|
|
395
|
+
def _ensure_loop(self) -> None:
|
|
396
|
+
# Fast path: loop already initialized
|
|
397
|
+
if self._loop_thread is not None and self._loop is not None:
|
|
398
|
+
return
|
|
399
|
+
|
|
400
|
+
with self._loop_init_lock:
|
|
401
|
+
# Double-check after acquiring lock
|
|
402
|
+
if self._loop_thread is not None and self._loop is not None:
|
|
403
|
+
return
|
|
404
|
+
self._loop_ready.clear()
|
|
405
|
+
self._loop_thread = threading.Thread(target=self._loop_runner, name="otel-loop", daemon=True)
|
|
406
|
+
self._loop_thread.start()
|
|
407
|
+
if not self._loop_ready.wait(timeout=30.0):
|
|
408
|
+
raise RuntimeError("Timed out waiting for otel-loop thread to start")
|
|
409
|
+
|
|
410
|
+
def _loop_runner(self):
|
|
411
|
+
loop = asyncio.new_event_loop()
|
|
412
|
+
self._loop = loop
|
|
413
|
+
asyncio.set_event_loop(loop)
|
|
414
|
+
self._loop_ready.set()
|
|
415
|
+
loop.run_forever()
|
|
416
|
+
loop.close()
|
|
417
|
+
|
|
418
|
+
def __enter__(self):
|
|
419
|
+
self._last_trace = None
|
|
420
|
+
self._spans = []
|
|
421
|
+
return self
|
|
422
|
+
|
|
423
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
|
|
424
|
+
self._store = None
|
|
425
|
+
self._rollout_id = None
|
|
426
|
+
self._attempt_id = None
|
|
427
|
+
|
|
428
|
+
def _await_in_loop(self, coro: Awaitable[Any], timeout: Optional[float] = None) -> Any:
|
|
429
|
+
# submit to the dedicated loop and wait synchronously
|
|
430
|
+
self._ensure_loop()
|
|
431
|
+
if self._loop is None:
|
|
432
|
+
raise RuntimeError("Loop is not initialized. This should not happen.")
|
|
433
|
+
|
|
434
|
+
# If already on the exporter loop thread, schedule and return immediately.
|
|
435
|
+
# ---------------------------------------------------------------------------
|
|
436
|
+
# WHY THIS CONDITIONAL EXISTS:
|
|
437
|
+
# In rare cases, span.end() is triggered from a LangchainCallbackHandler.__del__
|
|
438
|
+
# (or another finalizer) while the Python garbage collector is running on the
|
|
439
|
+
# *same thread* that owns our exporter event loop ("otel-loop").
|
|
440
|
+
#
|
|
441
|
+
# When that happens, on_end() executes on the exporter loop thread itself.
|
|
442
|
+
# If we were to call `asyncio.run_coroutine_threadsafe(...).result()` here,
|
|
443
|
+
# it would deadlock immediately — because the loop cannot both wait on and run
|
|
444
|
+
# the same coroutine. The Future stays pending forever and the loop stops
|
|
445
|
+
# processing scheduled callbacks.
|
|
446
|
+
#
|
|
447
|
+
# To avoid that self-deadlock, we detect when on_end() runs on the exporter
|
|
448
|
+
# loop thread. If so, we *schedule* the coroutine on the loop (fire-and-forget)
|
|
449
|
+
# instead of blocking with .result().
|
|
450
|
+
#
|
|
451
|
+
# This situation can occur because Python calls __del__ in whatever thread
|
|
452
|
+
# releases the last reference, which can easily be our loop thread if the
|
|
453
|
+
# object is dereferenced during loop._run_once().
|
|
454
|
+
# ---------------------------------------------------------------------------
|
|
455
|
+
if threading.current_thread() is self._loop_thread:
|
|
456
|
+
self._loop.call_soon_threadsafe(asyncio.create_task, coro) # type: ignore
|
|
457
|
+
return None
|
|
458
|
+
|
|
459
|
+
fut = asyncio.run_coroutine_threadsafe(coro, self._loop) # type: ignore
|
|
460
|
+
return fut.result(timeout=timeout) # raises on error # type: ignore
|
|
461
|
+
|
|
462
|
+
def shutdown(self) -> None:
|
|
463
|
+
if self._loop:
|
|
464
|
+
self._loop.call_soon_threadsafe(self._loop.stop)
|
|
465
|
+
self._loop = None
|
|
466
|
+
if self._loop_thread:
|
|
467
|
+
self._loop_thread.join(timeout=5)
|
|
468
|
+
|
|
469
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
470
|
+
return True
|
|
471
|
+
|
|
472
|
+
def spans(self) -> List[Span]:
|
|
473
|
+
"""
|
|
474
|
+
Get the list of spans collected by this processor.
|
|
475
|
+
This is useful for debugging and testing purposes.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
List of [`Span`][gatewaysdk.Span] objects collected during tracing.
|
|
479
|
+
"""
|
|
480
|
+
return self._spans
|
|
481
|
+
|
|
482
|
+
def with_context(self, store: LightningStore, rollout_id: str, attempt_id: str):
|
|
483
|
+
# simple context manager without nesting into asyncio
|
|
484
|
+
class _Ctx:
|
|
485
|
+
def __enter__(_): # type: ignore
|
|
486
|
+
# Use _ instead of self to avoid shadowing the instance method.
|
|
487
|
+
with self._lock:
|
|
488
|
+
self._store, self._rollout_id, self._attempt_id = store, rollout_id, attempt_id
|
|
489
|
+
self._last_trace = None
|
|
490
|
+
self._spans = []
|
|
491
|
+
return self
|
|
492
|
+
|
|
493
|
+
def __exit__(_, exc_type, exc, tb): # type: ignore
|
|
494
|
+
with self._lock:
|
|
495
|
+
self._store = self._rollout_id = self._attempt_id = None
|
|
496
|
+
|
|
497
|
+
return _Ctx()
|
|
498
|
+
|
|
499
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
500
|
+
"""
|
|
501
|
+
Process a span when it ends.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
span: The span that has ended.
|
|
505
|
+
"""
|
|
506
|
+
# Skip if span is not sampled
|
|
507
|
+
if not span.context or not span.context.trace_flags.sampled:
|
|
508
|
+
return
|
|
509
|
+
|
|
510
|
+
if not self._disable_store_submission and self._store and self._rollout_id and self._attempt_id:
|
|
511
|
+
try:
|
|
512
|
+
# Submit add_otel_span to the event loop and wait for it to complete
|
|
513
|
+
from opentelemetry.instrumentation.utils import suppress_instrumentation
|
|
514
|
+
|
|
515
|
+
with suppress_instrumentation():
|
|
516
|
+
self._ensure_loop()
|
|
517
|
+
uploaded_span = self._await_in_loop(
|
|
518
|
+
self._store.add_otel_span(self._rollout_id, self._attempt_id, span),
|
|
519
|
+
timeout=STORE_WRITE_TIMEOUT_SECONDS,
|
|
520
|
+
)
|
|
521
|
+
if uploaded_span is not None:
|
|
522
|
+
self._spans.append(uploaded_span)
|
|
523
|
+
except TimeoutError:
|
|
524
|
+
logger.warning(
|
|
525
|
+
"Timed out adding span %s to store after %.1f seconds. The span will be stored locally "
|
|
526
|
+
"but it's not guaranteed to be persisted.",
|
|
527
|
+
span.name,
|
|
528
|
+
STORE_WRITE_TIMEOUT_SECONDS,
|
|
529
|
+
)
|
|
530
|
+
self._spans.append(
|
|
531
|
+
Span.from_opentelemetry(
|
|
532
|
+
span,
|
|
533
|
+
rollout_id=self._rollout_id,
|
|
534
|
+
attempt_id=self._attempt_id,
|
|
535
|
+
sequence_id=self._local_sequence_id,
|
|
536
|
+
)
|
|
537
|
+
)
|
|
538
|
+
except Exception:
|
|
539
|
+
# log; on_end MUST NOT raise
|
|
540
|
+
logger.exception(f"Error adding span to store: {span.name}. The span will be store locally only.")
|
|
541
|
+
self._spans.append(
|
|
542
|
+
Span.from_opentelemetry(
|
|
543
|
+
span,
|
|
544
|
+
rollout_id=self._rollout_id,
|
|
545
|
+
attempt_id=self._attempt_id,
|
|
546
|
+
sequence_id=self._local_sequence_id,
|
|
547
|
+
)
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
else:
|
|
551
|
+
# Fallback path
|
|
552
|
+
created_span = Span.from_opentelemetry(
|
|
553
|
+
span,
|
|
554
|
+
rollout_id=self._rollout_id or "rollout-dummy",
|
|
555
|
+
attempt_id=self._attempt_id or "attempt-dummy",
|
|
556
|
+
sequence_id=self._local_sequence_id,
|
|
557
|
+
)
|
|
558
|
+
self._local_sequence_id += 1
|
|
559
|
+
self._spans.append(created_span)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Copyright (c) Gateway. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Standalone tracing module for GatewayDK.
|
|
4
|
+
|
|
5
|
+
This module provides a "one-liner" entry point for instrumenting applications
|
|
6
|
+
with OpenTelemetry, compatible with OpenInference ecosystem,
|
|
7
|
+
while natively supporting export to Gateway.
|
|
8
|
+
|
|
9
|
+
Example usage::
|
|
10
|
+
|
|
11
|
+
import gatewaysdk.tracing as tracing
|
|
12
|
+
|
|
13
|
+
# Auto-detect Gateway from environment variables
|
|
14
|
+
tracing.init()
|
|
15
|
+
|
|
16
|
+
# Use as a decorator (auto-captures input/output)
|
|
17
|
+
@tracing.trace
|
|
18
|
+
def my_function(query: str) -> dict:
|
|
19
|
+
return {"result": query.upper()}
|
|
20
|
+
|
|
21
|
+
# Use as a context manager (explicit input/output)
|
|
22
|
+
with tracing.trace("my-workflow", input=query) as span:
|
|
23
|
+
result = do_work()
|
|
24
|
+
span.set_attribute(tracing.semconv.TRACE_OUTPUT, result)
|
|
25
|
+
|
|
26
|
+
# Use semantic conventions for rich metadata
|
|
27
|
+
span.set_attribute(tracing.semconv.USER_ID, "user-123")
|
|
28
|
+
span.set_attribute(tracing.semconv.SESSION_ID, "session-456")
|
|
29
|
+
|
|
30
|
+
# Ensure spans are flushed on shutdown
|
|
31
|
+
tracing.shutdown()
|
|
32
|
+
|
|
33
|
+
Environment variables for Gateway auto-detect:
|
|
34
|
+
- GATEWAY_HOST: The Gateway server URL (e.g., "https://app.gateway.dev")
|
|
35
|
+
- GATEWAY_PUBLIC_KEY: The public key for authentication (pk-lf-...)
|
|
36
|
+
- GATEWAY_SECRET_KEY: The secret key for authentication (sk-lf-...)
|
|
37
|
+
- GATEWAY_OTLP_ENDPOINT: Optional override for the OTLP endpoint
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from . import semconv
|
|
41
|
+
from .api import aspan, atrace, get_current_span, span, tool, trace
|
|
42
|
+
|
|
43
|
+
# Re-export context managers and decorators for tagging traces
|
|
44
|
+
from .context import (
|
|
45
|
+
session,
|
|
46
|
+
call_type_decorator,
|
|
47
|
+
get_current_call_type,
|
|
48
|
+
get_current_environment,
|
|
49
|
+
get_current_tags,
|
|
50
|
+
tracing_context,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Re-export exporter factory for explicit configuration
|
|
54
|
+
from .exporters.gateway import gateway as gateway_exporter
|
|
55
|
+
from .init import flush, init, instrument, shutdown
|
|
56
|
+
|
|
57
|
+
# JSON push-to-trace
|
|
58
|
+
from .push import inspect, push, validate
|
|
59
|
+
from .push_models import (
|
|
60
|
+
ChildMapping,
|
|
61
|
+
FieldInfo,
|
|
62
|
+
InspectResult,
|
|
63
|
+
PushError,
|
|
64
|
+
PushResult,
|
|
65
|
+
SpanPreview,
|
|
66
|
+
TraceMapping,
|
|
67
|
+
TracePreview,
|
|
68
|
+
ValidationIssue,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
# Initialization
|
|
73
|
+
"init",
|
|
74
|
+
"instrument",
|
|
75
|
+
"shutdown",
|
|
76
|
+
"flush",
|
|
77
|
+
# Context managers / decorators (sync)
|
|
78
|
+
"trace",
|
|
79
|
+
"span",
|
|
80
|
+
"tool",
|
|
81
|
+
# Context managers (async)
|
|
82
|
+
"atrace",
|
|
83
|
+
"aspan",
|
|
84
|
+
# Span utilities
|
|
85
|
+
"get_current_span",
|
|
86
|
+
# Semantic conventions
|
|
87
|
+
"semconv",
|
|
88
|
+
# Exporter factory
|
|
89
|
+
"gateway_exporter",
|
|
90
|
+
# Tracing context and decorators
|
|
91
|
+
"session",
|
|
92
|
+
"tracing_context",
|
|
93
|
+
"call_type_decorator",
|
|
94
|
+
"get_current_call_type",
|
|
95
|
+
"get_current_tags",
|
|
96
|
+
"get_current_environment",
|
|
97
|
+
# JSON push-to-trace
|
|
98
|
+
"push",
|
|
99
|
+
"validate",
|
|
100
|
+
"inspect",
|
|
101
|
+
"TraceMapping",
|
|
102
|
+
"ChildMapping",
|
|
103
|
+
"PushResult",
|
|
104
|
+
"PushError",
|
|
105
|
+
"TracePreview",
|
|
106
|
+
"SpanPreview",
|
|
107
|
+
"ValidationIssue",
|
|
108
|
+
"InspectResult",
|
|
109
|
+
"FieldInfo",
|
|
110
|
+
]
|