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,527 @@
|
|
|
1
|
+
"""``gwenv.init`` and the :class:`Environment` object (spec §4).
|
|
2
|
+
|
|
3
|
+
import gatewaysdk.environment as gwenv
|
|
4
|
+
|
|
5
|
+
env = gwenv.init("refund-flows-env") # slug or id; READY head
|
|
6
|
+
with env: # implicit world + ambient interception
|
|
7
|
+
my_agent("handle ticket #4415")
|
|
8
|
+
result = env.verify("refund_row_created")
|
|
9
|
+
assert result.passed, env.state("SELECT * FROM refunds")
|
|
10
|
+
|
|
11
|
+
Local mode only in P1: the version bundle is pulled via the existing
|
|
12
|
+
environments/benchmark-hub clients, extracted to a cache dir, and executed
|
|
13
|
+
in-process (sqlite worlds for ``mock-tools``; per-task docker sandboxes for
|
|
14
|
+
``harbor``). ``mode="remote"`` and ``hosts=`` transport patching are P2/P3.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import inspect
|
|
20
|
+
import os
|
|
21
|
+
import tempfile
|
|
22
|
+
import time
|
|
23
|
+
from contextlib import contextmanager
|
|
24
|
+
from contextvars import ContextVar
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any, Dict, Iterable, Iterator, List, Mapping, Optional, Union
|
|
27
|
+
|
|
28
|
+
from .. import build as _build
|
|
29
|
+
from ..benchmark_hub.client import BenchmarkPushError, GatewayBenchmarkClient
|
|
30
|
+
from ..environments import GatewayEnvironmentClient, GatewayEnvironmentError
|
|
31
|
+
from ._bundle import (
|
|
32
|
+
KIND_HARBOR,
|
|
33
|
+
KIND_MOCK_TOOLS,
|
|
34
|
+
EnvManifest,
|
|
35
|
+
HarborTask,
|
|
36
|
+
detect_kind,
|
|
37
|
+
load_handlers,
|
|
38
|
+
load_harbor_tasks,
|
|
39
|
+
parse_manifest,
|
|
40
|
+
)
|
|
41
|
+
from ._harbor import CliDockerRuntime, DockerRuntime, HarborWorld
|
|
42
|
+
from ._world import _NO_INPUT, VerifierResult, World, WorldRegistry
|
|
43
|
+
from .errors import (
|
|
44
|
+
EnvironmentKindError,
|
|
45
|
+
EnvironmentNotConfigured,
|
|
46
|
+
EnvironmentNotReady,
|
|
47
|
+
HermeticViolation,
|
|
48
|
+
ToolRouteError,
|
|
49
|
+
WorldStartError,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
#: Env traffic records under ``gateway-env-{slug}`` — the tracing environment
|
|
53
|
+
#: tag that keeps it out of production views (sibling of replay's
|
|
54
|
+
#: ``gateway-replay``).
|
|
55
|
+
ENV_ENVIRONMENT_PREFIX = "gateway-env-"
|
|
56
|
+
|
|
57
|
+
_ROUTES = ("env", "prod")
|
|
58
|
+
|
|
59
|
+
#: The ambiently-active world (set by ``with env:`` / ``env.branch()``); the
|
|
60
|
+
#: ``define_tool`` wrapper consults it on every call.
|
|
61
|
+
_active_world: ContextVar[Optional[World]] = ContextVar("gateway_env_world", default=None)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_active_world() -> Optional[World]:
|
|
65
|
+
return _active_world.get()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RouteRecord:
|
|
69
|
+
"""One routed call: where it went and how long it took."""
|
|
70
|
+
|
|
71
|
+
__slots__ = ("tool", "route", "duration_ms", "world_id")
|
|
72
|
+
|
|
73
|
+
def __init__(self, tool: str, route: str, duration_ms: float, world_id: Optional[str]) -> None:
|
|
74
|
+
self.tool = tool
|
|
75
|
+
self.route = route
|
|
76
|
+
self.duration_ms = duration_ms
|
|
77
|
+
self.world_id = world_id
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str: # pragma: no cover — debugging nicety
|
|
80
|
+
return f"RouteRecord({self.tool!r}, {self.route!r}, {self.duration_ms:.1f}ms, world={self.world_id})"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Report:
|
|
84
|
+
"""Route provenance + hermetic accounting for one Environment."""
|
|
85
|
+
|
|
86
|
+
def __init__(self) -> None:
|
|
87
|
+
self.routes: List[RouteRecord] = []
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def hermetic(self) -> bool:
|
|
91
|
+
"""False if ANY call reached prod."""
|
|
92
|
+
return not self.prod_calls()
|
|
93
|
+
|
|
94
|
+
def prod_calls(self) -> List[RouteRecord]:
|
|
95
|
+
return [record for record in self.routes if record.route == "prod"]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Environment:
|
|
99
|
+
"""A resolved environment bundle plus its routing/runtime configuration.
|
|
100
|
+
|
|
101
|
+
Construct via :func:`init` (platform) or :func:`init_from_path` (local
|
|
102
|
+
bundle dir) rather than directly.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
bundle_path: Union[str, Path],
|
|
108
|
+
*,
|
|
109
|
+
slug: Optional[str] = None,
|
|
110
|
+
version: Optional[str] = None,
|
|
111
|
+
route: Optional[Mapping[str, str]] = None,
|
|
112
|
+
default_route: str = "env",
|
|
113
|
+
unsafe_allow_prod: Iterable[str] = (),
|
|
114
|
+
record: bool = True,
|
|
115
|
+
docker: Optional[DockerRuntime] = None,
|
|
116
|
+
) -> None:
|
|
117
|
+
self.bundle_path = Path(bundle_path)
|
|
118
|
+
if not self.bundle_path.is_dir():
|
|
119
|
+
raise EnvironmentNotReady(f"bundle path does not exist: {self.bundle_path}")
|
|
120
|
+
self.kind = detect_kind(self.bundle_path)
|
|
121
|
+
self.version = version
|
|
122
|
+
self.record = record
|
|
123
|
+
|
|
124
|
+
if default_route not in _ROUTES:
|
|
125
|
+
raise ValueError(f"default_route must be one of {_ROUTES}, got {default_route!r}")
|
|
126
|
+
self._route: Dict[str, str] = dict(route or {})
|
|
127
|
+
for tool_name, target in self._route.items():
|
|
128
|
+
if target not in _ROUTES:
|
|
129
|
+
raise ValueError(f"route[{tool_name!r}] must be one of {_ROUTES}, got {target!r}")
|
|
130
|
+
self._default_route = default_route
|
|
131
|
+
self._unsafe_allow_prod = set(unsafe_allow_prod)
|
|
132
|
+
self.report = Report()
|
|
133
|
+
self._docker_runtime = docker
|
|
134
|
+
|
|
135
|
+
self.manifest: Optional[EnvManifest] = None
|
|
136
|
+
self._handlers: Dict[str, Any] = {}
|
|
137
|
+
self._harbor_tasks: List[HarborTask] = []
|
|
138
|
+
self._implicit_world: Optional[World] = None
|
|
139
|
+
self._enter_token: Any = None
|
|
140
|
+
self._tracing_cm: Any = None
|
|
141
|
+
|
|
142
|
+
if self.kind == KIND_MOCK_TOOLS:
|
|
143
|
+
self.manifest = parse_manifest(self.bundle_path)
|
|
144
|
+
self.slug = slug or self.manifest.name
|
|
145
|
+
self._handlers = load_handlers(self.bundle_path, self.manifest)
|
|
146
|
+
self._tmpdir = tempfile.TemporaryDirectory(prefix=f"gwenv-{_safe(self.slug)}-")
|
|
147
|
+
self._worlds_dir = Path(self._tmpdir.name)
|
|
148
|
+
self._template_db = self._worlds_dir / "template.db"
|
|
149
|
+
self._build_template_db()
|
|
150
|
+
else:
|
|
151
|
+
self.slug = slug or self.bundle_path.name
|
|
152
|
+
self._harbor_tasks = load_harbor_tasks(self.bundle_path)
|
|
153
|
+
|
|
154
|
+
_install_ambient_interceptor()
|
|
155
|
+
|
|
156
|
+
# -- kind guards -----------------------------------------------------
|
|
157
|
+
|
|
158
|
+
def _require_mock(self, operation: str) -> None:
|
|
159
|
+
if self.kind != KIND_MOCK_TOOLS:
|
|
160
|
+
raise EnvironmentKindError(
|
|
161
|
+
f"{operation} is a mock-tools seam, but '{self.slug}' is a "
|
|
162
|
+
f"{self.kind} environment — harbor envs are task sandboxes, not "
|
|
163
|
+
"tool mocks. Use env.tasks / env.task(name), world.exec(...), "
|
|
164
|
+
"and world.verify() instead."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _require_harbor(self, operation: str) -> None:
|
|
168
|
+
if self.kind != KIND_HARBOR:
|
|
169
|
+
raise EnvironmentKindError(
|
|
170
|
+
f"{operation} is a harbor seam, but '{self.slug}' is a "
|
|
171
|
+
f"{self.kind} environment — use `with env:` / env.branch() and "
|
|
172
|
+
"the registry/execute seams instead."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# -- mock-tools: template + worlds ------------------------------------
|
|
176
|
+
|
|
177
|
+
def _build_template_db(self) -> None:
|
|
178
|
+
from ._materialize import materialize_template
|
|
179
|
+
|
|
180
|
+
materialize_template(self.bundle_path, self._template_db)
|
|
181
|
+
|
|
182
|
+
@contextmanager
|
|
183
|
+
def branch(self) -> Iterator[World]:
|
|
184
|
+
"""A fresh world (file copy of the seed) — run N is identical to run 1.
|
|
185
|
+
The world is also ambiently active inside the block."""
|
|
186
|
+
self._require_mock("env.branch()")
|
|
187
|
+
world = World(self)
|
|
188
|
+
token = _active_world.set(world)
|
|
189
|
+
try:
|
|
190
|
+
yield world
|
|
191
|
+
finally:
|
|
192
|
+
_active_world.reset(token)
|
|
193
|
+
|
|
194
|
+
def __enter__(self) -> "Environment":
|
|
195
|
+
self._require_mock("with env:")
|
|
196
|
+
if self._implicit_world is not None:
|
|
197
|
+
self._implicit_world.close()
|
|
198
|
+
self._implicit_world = World(self)
|
|
199
|
+
self._enter_token = _active_world.set(self._implicit_world)
|
|
200
|
+
self._tracing_cm = self._enter_tracing_context()
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
204
|
+
if self._enter_token is not None:
|
|
205
|
+
_active_world.reset(self._enter_token)
|
|
206
|
+
self._enter_token = None
|
|
207
|
+
if self._tracing_cm is not None:
|
|
208
|
+
try:
|
|
209
|
+
self._tracing_cm.__exit__(exc_type, exc, tb)
|
|
210
|
+
except Exception: # noqa: BLE001 — tracing is best-effort
|
|
211
|
+
pass
|
|
212
|
+
self._tracing_cm = None
|
|
213
|
+
# The implicit world stays alive: env.verify()/env.state() inspect the
|
|
214
|
+
# end state AFTER the block (spec §4.1 quickstart).
|
|
215
|
+
|
|
216
|
+
def _enter_tracing_context(self) -> Any:
|
|
217
|
+
"""Tag traces with environment=gateway-env-{slug} while inside
|
|
218
|
+
``with env:`` — best-effort, mirroring replay's REPLAY_ENVIRONMENT."""
|
|
219
|
+
try:
|
|
220
|
+
from ..tracing.exporters.gateway import is_gateway_configured
|
|
221
|
+
|
|
222
|
+
if not is_gateway_configured():
|
|
223
|
+
return None
|
|
224
|
+
from ..tracing.context import tracing_context
|
|
225
|
+
|
|
226
|
+
cm = tracing_context(environment=f"{ENV_ENVIRONMENT_PREFIX}{self.slug}")
|
|
227
|
+
cm.__enter__()
|
|
228
|
+
return cm
|
|
229
|
+
except Exception: # noqa: BLE001 — tracing is best-effort
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
def _ensure_world(self) -> World:
|
|
233
|
+
self._require_mock("the implicit world")
|
|
234
|
+
if self._implicit_world is None:
|
|
235
|
+
self._implicit_world = World(self)
|
|
236
|
+
return self._implicit_world
|
|
237
|
+
|
|
238
|
+
# -- delegates to the implicit world ----------------------------------
|
|
239
|
+
|
|
240
|
+
def execute(self, tool_name: str, tool_input: Any = _NO_INPUT) -> Any:
|
|
241
|
+
return self._ensure_world().execute(tool_name, tool_input)
|
|
242
|
+
|
|
243
|
+
def as_registry(self) -> WorldRegistry:
|
|
244
|
+
return self._ensure_world().as_registry()
|
|
245
|
+
|
|
246
|
+
def wrap_tools(self, tools: Any) -> Any:
|
|
247
|
+
return self._ensure_world().wrap_tools(tools)
|
|
248
|
+
|
|
249
|
+
def verify(self, name: str) -> VerifierResult:
|
|
250
|
+
return self._ensure_world().verify(name)
|
|
251
|
+
|
|
252
|
+
def state(self, sql: str, *params: Any) -> List[Dict[str, Any]]:
|
|
253
|
+
return self._ensure_world().state(sql, *params)
|
|
254
|
+
|
|
255
|
+
def reset(self) -> None:
|
|
256
|
+
self._ensure_world().reset()
|
|
257
|
+
|
|
258
|
+
# -- harbor ----------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def tasks(self) -> List[HarborTask]:
|
|
262
|
+
"""The env's Harbor tasks (harbor kind only)."""
|
|
263
|
+
self._require_harbor("env.tasks")
|
|
264
|
+
return list(self._harbor_tasks)
|
|
265
|
+
|
|
266
|
+
@contextmanager
|
|
267
|
+
def task(self, name: str) -> Iterator[HarborWorld]:
|
|
268
|
+
"""Boot one harbor task's docker sandbox; cleaned up on exit."""
|
|
269
|
+
self._require_harbor("env.task()")
|
|
270
|
+
matches = [task for task in self._harbor_tasks if task.name == name]
|
|
271
|
+
if not matches:
|
|
272
|
+
raise KeyError(
|
|
273
|
+
f"no harbor task '{name}' in '{self.slug}' "
|
|
274
|
+
f"(tasks: {[task.name for task in self._harbor_tasks]})"
|
|
275
|
+
)
|
|
276
|
+
world = HarborWorld(self, matches[0], self._docker())
|
|
277
|
+
try:
|
|
278
|
+
yield world
|
|
279
|
+
finally:
|
|
280
|
+
world.close()
|
|
281
|
+
|
|
282
|
+
def _docker(self) -> DockerRuntime:
|
|
283
|
+
if self._docker_runtime is None:
|
|
284
|
+
self._docker_runtime = CliDockerRuntime()
|
|
285
|
+
return self._docker_runtime
|
|
286
|
+
|
|
287
|
+
# -- routing ----------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
def resolve_route(self, tool_name: str) -> str:
|
|
290
|
+
"""Route map > effects default (read→default_route, write→env,
|
|
291
|
+
irreversible→never prod unless unsafe_allow_prod) > default_route."""
|
|
292
|
+
explicit = self._route.get(tool_name)
|
|
293
|
+
effects = _effects_of(tool_name)
|
|
294
|
+
if explicit is not None:
|
|
295
|
+
route = explicit
|
|
296
|
+
elif effects in ("write", "irreversible"):
|
|
297
|
+
route = "env"
|
|
298
|
+
else: # "read" or unregistered
|
|
299
|
+
route = self._default_route
|
|
300
|
+
if route == "prod" and effects == "irreversible" and tool_name not in self._unsafe_allow_prod:
|
|
301
|
+
raise ToolRouteError(
|
|
302
|
+
f"tool '{tool_name}' is declared effects='irreversible' — refusing "
|
|
303
|
+
"the prod route. Pass unsafe_allow_prod=['" + tool_name + "'] to "
|
|
304
|
+
"override (you probably don't want to)."
|
|
305
|
+
)
|
|
306
|
+
return route
|
|
307
|
+
|
|
308
|
+
def _record_route(self, tool: str, route: str, duration_ms: float, world_id: Optional[str]) -> None:
|
|
309
|
+
self.report.routes.append(RouteRecord(tool, route, duration_ms, world_id))
|
|
310
|
+
|
|
311
|
+
def assert_hermetic(self) -> None:
|
|
312
|
+
"""Raise :class:`HermeticViolation` if any call reached prod."""
|
|
313
|
+
offending = self.report.prod_calls()
|
|
314
|
+
if offending:
|
|
315
|
+
lines = "\n".join(
|
|
316
|
+
f" - {record.tool} ({record.duration_ms:.1f}ms)" for record in offending
|
|
317
|
+
)
|
|
318
|
+
raise HermeticViolation(
|
|
319
|
+
f"{len(offending)} call(s) reached prod during '{self.slug}':\n{lines}"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def close(self) -> None:
|
|
323
|
+
"""Release worlds and temp state (also happens at GC/interpreter exit)."""
|
|
324
|
+
if self._implicit_world is not None:
|
|
325
|
+
self._implicit_world.close()
|
|
326
|
+
self._implicit_world = None
|
|
327
|
+
if self.kind == KIND_MOCK_TOOLS:
|
|
328
|
+
self._tmpdir.cleanup()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _safe(name: str) -> str:
|
|
332
|
+
return "".join(ch if ch.isalnum() or ch in "-_" else "-" for ch in name)[:40]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _effects_of(tool_name: str) -> Optional[str]:
|
|
336
|
+
for identity in _build.get_registered_tools():
|
|
337
|
+
if identity.get("name") == tool_name:
|
|
338
|
+
return identity.get("effects")
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# ---------------------------------------------------------------------------
|
|
343
|
+
# Ambient interception — the define_tool wrapper's hook (spec §4.4, rung 1.5)
|
|
344
|
+
# ---------------------------------------------------------------------------
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _ambient_intercept(name: str, fn: Any, args: tuple, kwargs: dict) -> Any:
|
|
348
|
+
"""Called by the ``define_tool`` wrapper on every invocation. No active
|
|
349
|
+
world → miss sentinel (the wrapper calls the original fn — the identity
|
|
350
|
+
path). Active world → route: env executes the bundle handler against the
|
|
351
|
+
world; prod passes through to the original fn WITH provenance logged."""
|
|
352
|
+
world = _active_world.get()
|
|
353
|
+
if world is None:
|
|
354
|
+
return _build._TOOL_INTERCEPT_MISS
|
|
355
|
+
env = world.env
|
|
356
|
+
route = env.resolve_route(name)
|
|
357
|
+
|
|
358
|
+
if route == "env":
|
|
359
|
+
if name not in env._handlers:
|
|
360
|
+
raise ToolRouteError(
|
|
361
|
+
f"tool '{name}' routes to env but '{env.slug}' serves no handler "
|
|
362
|
+
f"for it (served: {sorted(env._handlers)}) — add a handler, "
|
|
363
|
+
"route={'" + name + "': 'prod'}, or default_route='prod'."
|
|
364
|
+
)
|
|
365
|
+
tool_input = _bind_tool_input(fn, args, kwargs)
|
|
366
|
+
if inspect.iscoroutinefunction(fn):
|
|
367
|
+
async def _async_execute() -> Any:
|
|
368
|
+
return world.execute(name, tool_input)
|
|
369
|
+
|
|
370
|
+
return _async_execute()
|
|
371
|
+
return world.execute(name, tool_input)
|
|
372
|
+
|
|
373
|
+
# prod passthrough — the original fn IS the live implementation.
|
|
374
|
+
started = time.perf_counter()
|
|
375
|
+
if inspect.iscoroutinefunction(fn):
|
|
376
|
+
async def _async_prod() -> Any:
|
|
377
|
+
try:
|
|
378
|
+
return await fn(*args, **kwargs)
|
|
379
|
+
finally:
|
|
380
|
+
env._record_route(name, "prod", (time.perf_counter() - started) * 1000.0, world.id)
|
|
381
|
+
|
|
382
|
+
return _async_prod()
|
|
383
|
+
try:
|
|
384
|
+
return fn(*args, **kwargs)
|
|
385
|
+
finally:
|
|
386
|
+
env._record_route(name, "prod", (time.perf_counter() - started) * 1000.0, world.id)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _bind_tool_input(fn: Any, args: tuple, kwargs: dict) -> Any:
|
|
390
|
+
"""Map the wrapped fn's call arguments to a kwargs dict for the env
|
|
391
|
+
handler (handlers mirror the real tool's parameter names)."""
|
|
392
|
+
if not args:
|
|
393
|
+
return dict(kwargs)
|
|
394
|
+
try:
|
|
395
|
+
bound = inspect.signature(fn).bind(*args, **kwargs)
|
|
396
|
+
return dict(bound.arguments)
|
|
397
|
+
except (TypeError, ValueError):
|
|
398
|
+
if len(args) == 1 and not kwargs:
|
|
399
|
+
return args[0]
|
|
400
|
+
return {"args": list(args), **kwargs}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
_interceptor_installed = False
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _install_ambient_interceptor() -> None:
|
|
407
|
+
global _interceptor_installed
|
|
408
|
+
if not _interceptor_installed:
|
|
409
|
+
_build.set_tool_call_interceptor(_ambient_intercept)
|
|
410
|
+
_interceptor_installed = True
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
# ---------------------------------------------------------------------------
|
|
414
|
+
# init
|
|
415
|
+
# ---------------------------------------------------------------------------
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def init_from_path(
|
|
419
|
+
path: Union[str, Path],
|
|
420
|
+
*,
|
|
421
|
+
slug: Optional[str] = None,
|
|
422
|
+
route: Optional[Mapping[str, str]] = None,
|
|
423
|
+
default_route: str = "env",
|
|
424
|
+
unsafe_allow_prod: Iterable[str] = (),
|
|
425
|
+
record: bool = True,
|
|
426
|
+
docker: Optional[DockerRuntime] = None,
|
|
427
|
+
) -> Environment:
|
|
428
|
+
"""Boot an environment straight from a local bundle directory — no
|
|
429
|
+
platform round-trip. The offline/dev/test twin of :func:`init`."""
|
|
430
|
+
return Environment(
|
|
431
|
+
path,
|
|
432
|
+
slug=slug,
|
|
433
|
+
route=route,
|
|
434
|
+
default_route=default_route,
|
|
435
|
+
unsafe_allow_prod=unsafe_allow_prod,
|
|
436
|
+
record=record,
|
|
437
|
+
docker=docker,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def init(
|
|
442
|
+
ref: str,
|
|
443
|
+
*,
|
|
444
|
+
version: Optional[str] = None,
|
|
445
|
+
mode: str = "local",
|
|
446
|
+
route: Optional[Mapping[str, str]] = None,
|
|
447
|
+
default_route: str = "env",
|
|
448
|
+
hosts: Optional[Mapping[str, str]] = None,
|
|
449
|
+
unsafe_allow_prod: Iterable[str] = (),
|
|
450
|
+
record: bool = True,
|
|
451
|
+
host: Optional[str] = None,
|
|
452
|
+
public_key: Optional[str] = None,
|
|
453
|
+
secret_key: Optional[str] = None,
|
|
454
|
+
docker: Optional[DockerRuntime] = None,
|
|
455
|
+
) -> Environment:
|
|
456
|
+
"""Resolve ``ref`` (env slug or container id) on the platform, pull the
|
|
457
|
+
READY version bundle (or ``version`` — pin in CI!), and boot it locally.
|
|
458
|
+
|
|
459
|
+
Missing keys/config raise :class:`EnvironmentNotConfigured` here, never
|
|
460
|
+
mid-run. ``mode="remote"`` (hosted worlds) and ``hosts=`` (transport-level
|
|
461
|
+
host routing) are not part of P1.
|
|
462
|
+
"""
|
|
463
|
+
if mode == "remote":
|
|
464
|
+
raise NotImplementedError(
|
|
465
|
+
"mode='remote' (platform-hosted worlds) lands with the mock-instance "
|
|
466
|
+
"manager — spec §4.9, phase P3. Use mode='local'."
|
|
467
|
+
)
|
|
468
|
+
if mode != "local":
|
|
469
|
+
raise ValueError(f"mode must be 'local' or 'remote', got {mode!r}")
|
|
470
|
+
if hosts:
|
|
471
|
+
raise NotImplementedError("hosts= transport routing lands in P2 — route tools, not hosts, for now.")
|
|
472
|
+
|
|
473
|
+
resolved_host = host or os.environ.get("GATEWAY_HOST") or ""
|
|
474
|
+
resolved_public = public_key or os.environ.get("GATEWAY_PUBLIC_KEY") or ""
|
|
475
|
+
resolved_secret = secret_key or os.environ.get("GATEWAY_SECRET_KEY") or ""
|
|
476
|
+
if not (resolved_host and resolved_public and resolved_secret):
|
|
477
|
+
raise EnvironmentNotConfigured(
|
|
478
|
+
"Gateway is not configured — set GATEWAY_HOST, GATEWAY_PUBLIC_KEY and "
|
|
479
|
+
"GATEWAY_SECRET_KEY (or pass host=/public_key=/secret_key=)."
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
env_client = GatewayEnvironmentClient(resolved_host, resolved_public, resolved_secret)
|
|
483
|
+
slug: Optional[str] = None
|
|
484
|
+
listing = env_client.list_environments(slug=ref)
|
|
485
|
+
if listing.environments:
|
|
486
|
+
slug = listing.environments[0].slug
|
|
487
|
+
else:
|
|
488
|
+
try:
|
|
489
|
+
slug = env_client.get_environment(ref).slug
|
|
490
|
+
except GatewayEnvironmentError:
|
|
491
|
+
raise EnvironmentNotReady(
|
|
492
|
+
f"environment '{ref}' not found (by slug or id) on {resolved_host}"
|
|
493
|
+
) from None
|
|
494
|
+
|
|
495
|
+
bench = GatewayBenchmarkClient(resolved_host, resolved_public, resolved_secret)
|
|
496
|
+
try:
|
|
497
|
+
details = bench.resolve_version(slug, version or "latest")
|
|
498
|
+
except BenchmarkPushError as error:
|
|
499
|
+
raise EnvironmentNotReady(f"no runnable version for '{slug}': {error}") from None
|
|
500
|
+
content_hash = details.get("contentHash")
|
|
501
|
+
if not details.get("downloadUrl"):
|
|
502
|
+
raise EnvironmentNotReady(
|
|
503
|
+
f"'{slug}'@{version or 'latest'} resolved but has no uploaded bundle — "
|
|
504
|
+
"the version never finished building (no READY head)."
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
cache_root = Path(
|
|
508
|
+
os.environ.get("GATEWAY_ENV_CACHE") or Path.home() / ".cache" / "gatewaysdk" / "environments"
|
|
509
|
+
)
|
|
510
|
+
bundle_dir = cache_root / slug / (content_hash or version or "latest")
|
|
511
|
+
if not (bundle_dir.is_dir() and any(bundle_dir.iterdir())):
|
|
512
|
+
bundle_dir.mkdir(parents=True, exist_ok=True)
|
|
513
|
+
try:
|
|
514
|
+
bench.pull(f"{slug}@{version or content_hash or 'latest'}", target=str(bundle_dir))
|
|
515
|
+
except BenchmarkPushError as error:
|
|
516
|
+
raise EnvironmentNotReady(f"failed to pull '{slug}' bundle: {error}") from None
|
|
517
|
+
|
|
518
|
+
return Environment(
|
|
519
|
+
bundle_dir,
|
|
520
|
+
slug=slug,
|
|
521
|
+
version=content_hash or version,
|
|
522
|
+
route=route,
|
|
523
|
+
default_route=default_route,
|
|
524
|
+
unsafe_allow_prod=unsafe_allow_prod,
|
|
525
|
+
record=record,
|
|
526
|
+
docker=docker,
|
|
527
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Errors for ``gatewaysdk.environment`` (spec §4.7).
|
|
2
|
+
|
|
3
|
+
All errors subclass :class:`GatewayEnvError` so callers can catch the whole
|
|
4
|
+
family with one except clause. The §4.7 set is fixed; ``EnvironmentKindError``
|
|
5
|
+
is an addendum addition for kind-mismatched API use (mock-tools seams on a
|
|
6
|
+
harbor env and vice versa).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GatewayEnvError(RuntimeError):
|
|
13
|
+
"""Base error for the environment SDK."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EnvironmentNotConfigured(GatewayEnvError):
|
|
17
|
+
"""Missing host/keys — raised at ``init()``, never mid-run."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EnvironmentNotReady(GatewayEnvError):
|
|
21
|
+
"""No READY version to run against (or the env/bundle cannot be resolved)."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class WorldStartError(GatewayEnvError):
|
|
25
|
+
"""A world failed to boot (sqlite template build, docker run, …) — the
|
|
26
|
+
message carries a remediation hint."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ToolRouteError(GatewayEnvError):
|
|
30
|
+
"""A tool call had nowhere to go: no env handler and no prod route, or a
|
|
31
|
+
route the current seam cannot honor (e.g. prod at the registry seam)."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class HermeticViolation(GatewayEnvError):
|
|
35
|
+
"""Raised by ``env.assert_hermetic()`` when any call reached prod —
|
|
36
|
+
the message lists the offending calls."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class VerifierError(GatewayEnvError):
|
|
40
|
+
"""Unknown verifier or verifier execution error, with the SQL/script
|
|
41
|
+
echoed for debugging."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class EnvironmentKindError(GatewayEnvError):
|
|
45
|
+
"""An API surface was used on the wrong environment kind (harbor task
|
|
46
|
+
sandboxes vs mock-tools worlds)."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"GatewayEnvError",
|
|
51
|
+
"EnvironmentNotConfigured",
|
|
52
|
+
"EnvironmentNotReady",
|
|
53
|
+
"WorldStartError",
|
|
54
|
+
"ToolRouteError",
|
|
55
|
+
"HermeticViolation",
|
|
56
|
+
"VerifierError",
|
|
57
|
+
"EnvironmentKindError",
|
|
58
|
+
]
|