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
gatewaysdk/worlds.py
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
"""World-over-tools: run an agent's real tool surface against a synced world.
|
|
2
|
+
|
|
3
|
+
The dev-loop story in one file:
|
|
4
|
+
|
|
5
|
+
from gatewaysdk import worlds
|
|
6
|
+
|
|
7
|
+
world = worlds.load("google-drive-sim@wip-charles", variant="alice")
|
|
8
|
+
|
|
9
|
+
@world.over(prod_list_files) # same name, same signature
|
|
10
|
+
def list_files(folder=None): ...
|
|
11
|
+
|
|
12
|
+
# wrapped calls now read/write alice's SEEDED database instead of prod;
|
|
13
|
+
# with no world loaded the decorator is inert and prod impls run.
|
|
14
|
+
|
|
15
|
+
result = world.run(my_agent) # grade the end state, file the
|
|
16
|
+
# rollout back as a version-pinned
|
|
17
|
+
# run (branch tag included)
|
|
18
|
+
|
|
19
|
+
A world is a pulled container bundle (branch-aware — `slug@branch`), one task
|
|
20
|
+
VARIANT of it (its own database, materialized fresh from the task's seed), and
|
|
21
|
+
the bundle's own tool handlers bound to that database. Wrapping maps a prod
|
|
22
|
+
tool to the bundle handler of the SAME name and fails loudly on a tool the
|
|
23
|
+
world does not serve — a silent passthrough would let a "world run" quietly
|
|
24
|
+
hit production, which is the exact accident this module exists to prevent.
|
|
25
|
+
|
|
26
|
+
Conventions a bundle opts into (see samples/environments/google-drive-sim):
|
|
27
|
+
* task dirs carry ``[metadata] variant = "<name>"`` in task.toml;
|
|
28
|
+
* ``environment/`` holds ``schema.sql`` + ``seed.sql`` + ``tools/`` with a
|
|
29
|
+
module exposing ``TOOLS = {name: callable}`` and optionally
|
|
30
|
+
``DB_ENV_VAR`` naming the env var its tools read the db path from
|
|
31
|
+
(default ``GATEWAY_WORLD_DB``);
|
|
32
|
+
* ``environment/grade.py`` reads ``DRIVE_DB``-style env + ``GRADE_OUT`` and
|
|
33
|
+
writes ``reward.json`` — which makes SDK grading identical to the harbor
|
|
34
|
+
container's own verifier.
|
|
35
|
+
|
|
36
|
+
Stdlib only, like the rest of benchmark_hub.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import functools
|
|
42
|
+
import importlib.util
|
|
43
|
+
import json
|
|
44
|
+
import os
|
|
45
|
+
import re
|
|
46
|
+
import sqlite3
|
|
47
|
+
import subprocess
|
|
48
|
+
import sys
|
|
49
|
+
import tempfile
|
|
50
|
+
import time
|
|
51
|
+
from pathlib import Path
|
|
52
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
53
|
+
|
|
54
|
+
from .benchmark_hub.client import (
|
|
55
|
+
BenchmarkPushError,
|
|
56
|
+
GatewayBenchmarkClient,
|
|
57
|
+
local_tree_state,
|
|
58
|
+
)
|
|
59
|
+
from .benchmark_hub.evals import GatewayEvalsClient
|
|
60
|
+
|
|
61
|
+
try: # py311+
|
|
62
|
+
import tomllib
|
|
63
|
+
except ImportError: # pragma: no cover
|
|
64
|
+
tomllib = None # type: ignore[assignment]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class WorldError(RuntimeError):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _task_variant(task_dir: Path) -> Optional[str]:
|
|
72
|
+
toml_path = task_dir / "task.toml"
|
|
73
|
+
if not toml_path.is_file():
|
|
74
|
+
return None
|
|
75
|
+
if tomllib is not None:
|
|
76
|
+
try:
|
|
77
|
+
data = tomllib.loads(toml_path.read_text(encoding="utf-8"))
|
|
78
|
+
v = (data.get("metadata") or {}).get("variant")
|
|
79
|
+
return str(v) if v else None
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
m = re.search(r'^\s*variant\s*=\s*"([^"]+)"', toml_path.read_text(encoding="utf-8"), re.M)
|
|
83
|
+
return m.group(1) if m else None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _pick_task(bundle: Path, variant: Optional[str]) -> Path:
|
|
87
|
+
tasks = sorted(p for p in (bundle / "tasks").iterdir() if (p / "task.toml").is_file()) if (bundle / "tasks").is_dir() else []
|
|
88
|
+
if not tasks:
|
|
89
|
+
raise WorldError(f"{bundle} has no tasks/*/task.toml — not a harbor world")
|
|
90
|
+
if variant is None:
|
|
91
|
+
return tasks[0]
|
|
92
|
+
for t in tasks:
|
|
93
|
+
if _task_variant(t) == variant:
|
|
94
|
+
return t
|
|
95
|
+
for t in tasks: # dir-name fallback, per the accepted identity rule
|
|
96
|
+
if variant in t.name:
|
|
97
|
+
return t
|
|
98
|
+
known = [f"{t.name} (variant={_task_variant(t) or '—'})" for t in tasks]
|
|
99
|
+
raise WorldError(f"no task matches variant {variant!r}; have: {', '.join(known)}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def tasks(bundle_dir: str | Path) -> List[Dict[str, str]]:
|
|
103
|
+
"""Rich task listing for a bundle: one dict per task with its name,
|
|
104
|
+
variant, and instruction — what a suite runner or a notebook needs to
|
|
105
|
+
work with tasks as data instead of shelling out to the CLI.
|
|
106
|
+
|
|
107
|
+
first = worlds.load("google-drive-sim@main")
|
|
108
|
+
for task in worlds.tasks(first.bundle_dir):
|
|
109
|
+
print(task["variant"], "—", task["instruction"][:60])
|
|
110
|
+
"""
|
|
111
|
+
bundle = Path(bundle_dir)
|
|
112
|
+
listing: List[Dict[str, str]] = []
|
|
113
|
+
for name in variants(bundle):
|
|
114
|
+
task_dir = _pick_task(bundle, name)
|
|
115
|
+
instruction_file = task_dir / "instruction.md"
|
|
116
|
+
listing.append(
|
|
117
|
+
{
|
|
118
|
+
"name": task_dir.name,
|
|
119
|
+
"variant": name,
|
|
120
|
+
"instruction": (
|
|
121
|
+
instruction_file.read_text(encoding="utf-8")
|
|
122
|
+
if instruction_file.is_file()
|
|
123
|
+
else ""
|
|
124
|
+
),
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
return listing
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def variants(bundle_dir: str | Path) -> List[str]:
|
|
131
|
+
"""Variant names a bundle serves, in task order — one entry per
|
|
132
|
+
``tasks/*/task.toml``, named by the task's own variant when it declares
|
|
133
|
+
one and by the directory otherwise. This is the suite-runner's loop:
|
|
134
|
+
|
|
135
|
+
first = worlds.load("google-drive-sim@main")
|
|
136
|
+
for name in worlds.variants(first.bundle_dir):
|
|
137
|
+
world = worlds.load_path(first.bundle_dir, variant=name)
|
|
138
|
+
"""
|
|
139
|
+
bundle = Path(bundle_dir)
|
|
140
|
+
tasks = (
|
|
141
|
+
sorted(p for p in (bundle / "tasks").iterdir() if (p / "task.toml").is_file())
|
|
142
|
+
if (bundle / "tasks").is_dir()
|
|
143
|
+
else []
|
|
144
|
+
)
|
|
145
|
+
if not tasks:
|
|
146
|
+
raise WorldError(f"{bundle} has no tasks/*/task.toml — not a harbor world")
|
|
147
|
+
return [_task_variant(t) or t.name for t in tasks]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _load_tools_module(task_dir: Path):
|
|
151
|
+
tools_dir = task_dir / "environment" / "tools"
|
|
152
|
+
candidates = sorted(tools_dir.glob("*.py")) if tools_dir.is_dir() else []
|
|
153
|
+
for path in candidates:
|
|
154
|
+
if path.name.startswith("_"):
|
|
155
|
+
continue
|
|
156
|
+
spec = importlib.util.spec_from_file_location(f"gateway_world_tools_{path.stem}_{id(path)}", path)
|
|
157
|
+
if spec is None or spec.loader is None:
|
|
158
|
+
continue
|
|
159
|
+
module = importlib.util.module_from_spec(spec)
|
|
160
|
+
spec.loader.exec_module(module)
|
|
161
|
+
if hasattr(module, "TOOLS") and isinstance(module.TOOLS, dict):
|
|
162
|
+
return module
|
|
163
|
+
raise WorldError(
|
|
164
|
+
f"{task_dir.name} serves no tools module (environment/tools/*.py exposing TOOLS)"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class World:
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
*,
|
|
172
|
+
bundle_dir: Path,
|
|
173
|
+
task_dir: Path,
|
|
174
|
+
variant: Optional[str],
|
|
175
|
+
slug: Optional[str],
|
|
176
|
+
branch: Optional[str],
|
|
177
|
+
version_id: Optional[str],
|
|
178
|
+
file_block: Optional[str] = None,
|
|
179
|
+
) -> None:
|
|
180
|
+
from .environment.schema.compiler import require_legacy_bundle
|
|
181
|
+
|
|
182
|
+
require_legacy_bundle(bundle_dir, "Harbor world sessions")
|
|
183
|
+
self.bundle_dir = bundle_dir
|
|
184
|
+
self.task_dir = task_dir
|
|
185
|
+
self.variant = variant
|
|
186
|
+
self.slug = slug
|
|
187
|
+
self.branch = branch
|
|
188
|
+
self.version_id = version_id
|
|
189
|
+
# Why a run of THIS world cannot be filed (dirty checkout, no hub
|
|
190
|
+
# link). None whenever version_id is set.
|
|
191
|
+
self.file_block = file_block
|
|
192
|
+
self.call_log: List[Dict[str, Any]] = []
|
|
193
|
+
|
|
194
|
+
env_dir = task_dir / "environment"
|
|
195
|
+
schema = env_dir / "schema.sql"
|
|
196
|
+
if not schema.is_file():
|
|
197
|
+
raise WorldError(f"{task_dir.name} has no environment/schema.sql to materialize")
|
|
198
|
+
self.db_path = str(Path(tempfile.mkdtemp(prefix="gateway-world-")) / "world.db")
|
|
199
|
+
conn = sqlite3.connect(self.db_path)
|
|
200
|
+
try:
|
|
201
|
+
conn.executescript(schema.read_text(encoding="utf-8"))
|
|
202
|
+
seed = env_dir / "seed.sql"
|
|
203
|
+
if seed.is_file():
|
|
204
|
+
conn.executescript(seed.read_text(encoding="utf-8"))
|
|
205
|
+
conn.commit()
|
|
206
|
+
finally:
|
|
207
|
+
conn.close()
|
|
208
|
+
|
|
209
|
+
self._module = _load_tools_module(task_dir)
|
|
210
|
+
self._db_env_var = getattr(self._module, "DB_ENV_VAR", "GATEWAY_WORLD_DB")
|
|
211
|
+
os.environ[self._db_env_var] = self.db_path
|
|
212
|
+
|
|
213
|
+
# Handlers bound to THIS world, each recording into the call log so an
|
|
214
|
+
# SDK run files a real trajectory, not just a score.
|
|
215
|
+
self.tools: Dict[str, Callable[..., Any]] = {
|
|
216
|
+
name: self._record(name, fn) for name, fn in self._module.TOOLS.items()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
def _record(self, name: str, fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
220
|
+
@functools.wraps(fn)
|
|
221
|
+
def recorded(*args: Any, **kwargs: Any) -> Any:
|
|
222
|
+
entry: Dict[str, Any] = {
|
|
223
|
+
"tool": name,
|
|
224
|
+
"arguments": json.dumps({"args": args, "kwargs": kwargs}, default=str)[:2000],
|
|
225
|
+
}
|
|
226
|
+
os.environ[self._db_env_var] = self.db_path
|
|
227
|
+
try:
|
|
228
|
+
result = fn(*args, **kwargs)
|
|
229
|
+
entry["result_preview"] = json.dumps(result, default=str)[:2000]
|
|
230
|
+
return result
|
|
231
|
+
except Exception as exc:
|
|
232
|
+
entry["error"] = str(exc)[:500]
|
|
233
|
+
raise
|
|
234
|
+
finally:
|
|
235
|
+
self.call_log.append(entry)
|
|
236
|
+
|
|
237
|
+
return recorded
|
|
238
|
+
|
|
239
|
+
def over(
|
|
240
|
+
self, prod_fn: Callable[..., Any], *, name: Optional[str] = None
|
|
241
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
242
|
+
"""Decorator: route calls to the world's handler of the same name.
|
|
243
|
+
|
|
244
|
+
``@world.over(prod_list_files)`` on a function keeps the prod
|
|
245
|
+
implementation as the fallback ONLY when this world is inert (never
|
|
246
|
+
constructed / explicitly disabled) — a loaded world that does not
|
|
247
|
+
serve the tool raises instead of silently touching production.
|
|
248
|
+
"""
|
|
249
|
+
def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
250
|
+
# The DECORATED function's name is the agent's tool name; the prod
|
|
251
|
+
# fn is only the implementation being displaced.
|
|
252
|
+
tool_name = name or fn.__name__
|
|
253
|
+
if tool_name not in self.tools:
|
|
254
|
+
raise WorldError(
|
|
255
|
+
f"world {self.slug or self.bundle_dir.name!r} does not serve "
|
|
256
|
+
f"tool {tool_name!r}; it serves: {', '.join(sorted(self.tools))}"
|
|
257
|
+
)
|
|
258
|
+
world_fn = self.tools[tool_name]
|
|
259
|
+
|
|
260
|
+
@functools.wraps(prod_fn)
|
|
261
|
+
def routed(*args: Any, **kwargs: Any) -> Any:
|
|
262
|
+
return world_fn(*args, **kwargs)
|
|
263
|
+
|
|
264
|
+
return routed
|
|
265
|
+
|
|
266
|
+
return decorate
|
|
267
|
+
|
|
268
|
+
def wrap(self, prod_tools: Dict[str, Callable[..., Any]]) -> Dict[str, Callable[..., Any]]:
|
|
269
|
+
"""Wholesale mapping: {name: prod_fn} → {name: world_fn}, fail-loud on
|
|
270
|
+
any name the world does not serve."""
|
|
271
|
+
missing = sorted(set(prod_tools) - set(self.tools))
|
|
272
|
+
if missing:
|
|
273
|
+
raise WorldError(
|
|
274
|
+
f"world does not serve: {', '.join(missing)}; it serves: {', '.join(sorted(self.tools))}"
|
|
275
|
+
)
|
|
276
|
+
return {name: self.tools[name] for name in prod_tools}
|
|
277
|
+
|
|
278
|
+
def override(
|
|
279
|
+
self, name: str, fn: Callable[..., Any]
|
|
280
|
+
) -> Callable[..., Any]:
|
|
281
|
+
"""The INVERSE of wrap(): keep the world's seeded database, tasks and
|
|
282
|
+
graders — swap ONE tool's implementation with your LOCAL one. This is
|
|
283
|
+
the schema-stable dev loop for tool logic changes: the local fn reads
|
|
284
|
+
the same database the world materialized (the module convention — the
|
|
285
|
+
env var named by ``DB_ENV_VAR`` already points at it), the task's own
|
|
286
|
+
grader judges the end state, and calls are recorded into the filed
|
|
287
|
+
call log like any world tool. Unknown names fail loudly: overriding a
|
|
288
|
+
tool the world does not serve would silently test nothing."""
|
|
289
|
+
if name not in self.tools:
|
|
290
|
+
raise WorldError(
|
|
291
|
+
f"world does not serve a tool named {name!r}; it serves: "
|
|
292
|
+
f"{', '.join(sorted(self.tools))}"
|
|
293
|
+
)
|
|
294
|
+
recorded = self._record(name, fn)
|
|
295
|
+
self.tools[name] = recorded
|
|
296
|
+
return recorded
|
|
297
|
+
|
|
298
|
+
def grade(self) -> Dict[str, Any]:
|
|
299
|
+
"""Run the task's OWN grader against the world's end state — the same
|
|
300
|
+
code the harbor container runs, pointed at the materialized db."""
|
|
301
|
+
grader = self.task_dir / "environment" / "grade.py"
|
|
302
|
+
if not grader.is_file():
|
|
303
|
+
raise WorldError(f"{self.task_dir.name} ships no environment/grade.py")
|
|
304
|
+
out_dir = tempfile.mkdtemp(prefix="gateway-grade-")
|
|
305
|
+
env = dict(os.environ)
|
|
306
|
+
env[self._db_env_var] = self.db_path
|
|
307
|
+
env["DRIVE_DB"] = env.get("DRIVE_DB", self.db_path)
|
|
308
|
+
env["GRADE_OUT"] = out_dir
|
|
309
|
+
proc = subprocess.run(
|
|
310
|
+
[sys.executable, str(grader)], env=env, capture_output=True, text=True, timeout=120
|
|
311
|
+
)
|
|
312
|
+
reward_path = Path(out_dir) / "reward.json"
|
|
313
|
+
if proc.returncode != 0 or not reward_path.is_file():
|
|
314
|
+
raise WorldError(
|
|
315
|
+
f"grader failed (exit {proc.returncode}): {proc.stderr.strip()[:400]}"
|
|
316
|
+
)
|
|
317
|
+
return json.loads(reward_path.read_text(encoding="utf-8"))
|
|
318
|
+
|
|
319
|
+
def run(
|
|
320
|
+
self,
|
|
321
|
+
agent_fn: Callable[["World"], Any],
|
|
322
|
+
*,
|
|
323
|
+
model: str = "sdk-dev-loop",
|
|
324
|
+
file: bool = True,
|
|
325
|
+
host: Optional[str] = None,
|
|
326
|
+
session_id: Optional[str] = None,
|
|
327
|
+
) -> Dict[str, Any]:
|
|
328
|
+
"""Execute ``agent_fn(world)``, grade the end state, and (by default)
|
|
329
|
+
file the result back as a version-pinned run — landing on the same
|
|
330
|
+
Runs/Rollouts surfaces as hosted runs, branch tag included (the
|
|
331
|
+
server stamps metadata.ref from the pinned version).
|
|
332
|
+
|
|
333
|
+
Pass ``session_id`` when the agent traces itself (the id given to
|
|
334
|
+
``tracing.session(...)``): the platform reads ``info.sessionId`` off
|
|
335
|
+
the sample to link the run to the traced session — that link is the
|
|
336
|
+
trajectory. The filed sample carries the task name, the instruction
|
|
337
|
+
as the prompt, and ``agent_fn``'s return value as the completion, so
|
|
338
|
+
the run page shows the episode, not an empty row."""
|
|
339
|
+
started = time.time()
|
|
340
|
+
agent_error: Optional[str] = None
|
|
341
|
+
final: Any = None
|
|
342
|
+
try:
|
|
343
|
+
final = agent_fn(self)
|
|
344
|
+
except Exception as exc:
|
|
345
|
+
agent_error = str(exc)[:500]
|
|
346
|
+
verdict = self.grade()
|
|
347
|
+
reward = verdict.get("reward")
|
|
348
|
+
subs = {k: v for k, v in verdict.items() if k != "reward"}
|
|
349
|
+
result: Dict[str, Any] = {
|
|
350
|
+
"reward": reward,
|
|
351
|
+
"rewards": subs,
|
|
352
|
+
"agentError": agent_error,
|
|
353
|
+
"calls": len(self.call_log),
|
|
354
|
+
"durationSeconds": round(time.time() - started, 3),
|
|
355
|
+
**({"sessionId": session_id} if session_id else {}),
|
|
356
|
+
}
|
|
357
|
+
if not file:
|
|
358
|
+
return result
|
|
359
|
+
if not self.version_id:
|
|
360
|
+
raise WorldError(
|
|
361
|
+
self.file_block
|
|
362
|
+
or "this world was loaded from a local path — pass file=False, "
|
|
363
|
+
"or load it from the hub so runs can pin a version"
|
|
364
|
+
)
|
|
365
|
+
instruction_file = self.task_dir / "instruction.md"
|
|
366
|
+
instruction = (
|
|
367
|
+
instruction_file.read_text(encoding="utf-8")
|
|
368
|
+
if instruction_file.is_file()
|
|
369
|
+
else ""
|
|
370
|
+
)
|
|
371
|
+
evals = GatewayEvalsClient.from_env(host)
|
|
372
|
+
evaluation_id = evals.create_evaluation(
|
|
373
|
+
self.version_id,
|
|
374
|
+
model=model,
|
|
375
|
+
rollouts_per_example=1,
|
|
376
|
+
metadata={
|
|
377
|
+
"source": "sdk-world-run",
|
|
378
|
+
"variant": self.variant,
|
|
379
|
+
**({"sessionId": session_id} if session_id else {}),
|
|
380
|
+
},
|
|
381
|
+
)
|
|
382
|
+
evals.push_samples(
|
|
383
|
+
evaluation_id,
|
|
384
|
+
[
|
|
385
|
+
{
|
|
386
|
+
"exampleId": self.task_dir.name,
|
|
387
|
+
"task": self.task_dir.name,
|
|
388
|
+
"rolloutIndex": 0,
|
|
389
|
+
"prompt": instruction[:20000] or None,
|
|
390
|
+
"completion": (
|
|
391
|
+
str(final)[:20000] if final is not None else None
|
|
392
|
+
),
|
|
393
|
+
"reward": reward,
|
|
394
|
+
"score": reward,
|
|
395
|
+
"correct": (reward >= 1.0) if isinstance(reward, (int, float)) else None,
|
|
396
|
+
"rewards": subs,
|
|
397
|
+
"info": {
|
|
398
|
+
"variant": self.variant,
|
|
399
|
+
"source": "sdk-world-run",
|
|
400
|
+
"agentError": agent_error,
|
|
401
|
+
"callLog": self.call_log[-50:],
|
|
402
|
+
# The platform's session link + token-usage join read
|
|
403
|
+
# exactly this key.
|
|
404
|
+
**({"sessionId": session_id} if session_id else {}),
|
|
405
|
+
},
|
|
406
|
+
}
|
|
407
|
+
],
|
|
408
|
+
)
|
|
409
|
+
evals.finalize_evaluation(
|
|
410
|
+
evaluation_id,
|
|
411
|
+
status="FAILED" if agent_error else "COMPLETED",
|
|
412
|
+
metrics={
|
|
413
|
+
# avg_reward is what the Runs list renders as the score —
|
|
414
|
+
# a finalize without it files a run that LOOKS unscored.
|
|
415
|
+
**({"avg_reward": reward} if isinstance(reward, (int, float)) else {}),
|
|
416
|
+
"reward": reward,
|
|
417
|
+
**{f"rewards.{k}": v for k, v in subs.items()},
|
|
418
|
+
},
|
|
419
|
+
)
|
|
420
|
+
result["evaluationId"] = evaluation_id
|
|
421
|
+
return result
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def load_path(bundle_dir: str | Path, *, variant: Optional[str] = None) -> World:
|
|
425
|
+
"""Load a world from a LOCAL directory. A dir that is a hub checkout
|
|
426
|
+
(`gateway bench pull`/`checkout`/`push` wrote `.gateway/`) whose tree
|
|
427
|
+
still matches its recorded base loads with that base's slug/branch/
|
|
428
|
+
version — runs file exactly like `load()`. A drifted checkout or an
|
|
429
|
+
ad-hoc dir still runs and grades, but filing is refused with the reason
|
|
430
|
+
(pinning drifted code to a version would fabricate provenance)."""
|
|
431
|
+
bundle = Path(bundle_dir)
|
|
432
|
+
task = _pick_task(bundle, variant)
|
|
433
|
+
state = local_tree_state(bundle)
|
|
434
|
+
meta = state["meta"] or {}
|
|
435
|
+
clean = bool(state["clean"]) and bool(meta.get("versionId"))
|
|
436
|
+
if clean:
|
|
437
|
+
slug, branch, version_id, file_block = (
|
|
438
|
+
meta.get("slug"),
|
|
439
|
+
meta.get("branch") or meta.get("ref"),
|
|
440
|
+
meta.get("versionId"),
|
|
441
|
+
None,
|
|
442
|
+
)
|
|
443
|
+
elif meta.get("versionId"):
|
|
444
|
+
dirty: List[str] = state["dirty"]
|
|
445
|
+
shown = ", ".join(dirty[:5]) + (", …" if len(dirty) > 5 else "")
|
|
446
|
+
slug, branch, version_id = meta.get("slug"), None, None
|
|
447
|
+
file_block = (
|
|
448
|
+
f"this checkout differs from "
|
|
449
|
+
f"{meta.get('branch') or meta.get('ref') or 'its base'}@"
|
|
450
|
+
f"{str(meta.get('contentHash') or '')[:8]} in {len(dirty)} "
|
|
451
|
+
f"file(s) ({shown}) — push first (`gateway bench push`) so the "
|
|
452
|
+
f"run pins the code it actually executed, or pass file=False"
|
|
453
|
+
)
|
|
454
|
+
else:
|
|
455
|
+
slug, branch, version_id = None, None, None
|
|
456
|
+
file_block = (
|
|
457
|
+
"this directory has no hub link (.gateway/.env-metadata.json) — "
|
|
458
|
+
"push it (`gateway bench push`) so runs can pin a version, or "
|
|
459
|
+
"pass file=False"
|
|
460
|
+
)
|
|
461
|
+
return World(
|
|
462
|
+
bundle_dir=bundle,
|
|
463
|
+
task_dir=task,
|
|
464
|
+
variant=variant or _task_variant(task) or task.name,
|
|
465
|
+
slug=slug,
|
|
466
|
+
branch=branch,
|
|
467
|
+
version_id=version_id,
|
|
468
|
+
file_block=file_block,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def load(
|
|
473
|
+
slug_ref: str,
|
|
474
|
+
*,
|
|
475
|
+
variant: Optional[str] = None,
|
|
476
|
+
host: Optional[str] = None,
|
|
477
|
+
) -> World:
|
|
478
|
+
"""Pull ``slug`` / ``slug@branch|tag|semver|hash`` from the hub and load
|
|
479
|
+
one variant of it. The pulled branch travels with the world, so filed
|
|
480
|
+
runs carry it."""
|
|
481
|
+
client = GatewayBenchmarkClient.from_env(host)
|
|
482
|
+
target = tempfile.mkdtemp(prefix="gateway-world-pull-")
|
|
483
|
+
pulled = client.pull(slug_ref, target=target)
|
|
484
|
+
bundle = Path(pulled["target"])
|
|
485
|
+
task = _pick_task(bundle, variant)
|
|
486
|
+
return World(
|
|
487
|
+
bundle_dir=bundle,
|
|
488
|
+
task_dir=task,
|
|
489
|
+
variant=variant or _task_variant(task) or task.name,
|
|
490
|
+
slug=pulled.get("slug"),
|
|
491
|
+
branch=pulled.get("branch"),
|
|
492
|
+
version_id=pulled.get("versionId"),
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# ── Object model: WorldSet / Task / TaskEnv ──────────────────────────────────
|
|
496
|
+
# `worlds.open()` is the handle FOR THE WORLD (one pull, one version pin);
|
|
497
|
+
# `Task` is the scenario as data; `TaskEnv` (== the classic `World`) is one
|
|
498
|
+
# task's materialized instance. `worlds.load()` remains sugar over this and
|
|
499
|
+
# behaves exactly as before.
|
|
500
|
+
|
|
501
|
+
TaskEnv = World
|
|
502
|
+
"""The materialized per-task instance — seeded db, recorded tools, grader.
|
|
503
|
+
The class historically named `World`; both names stay valid."""
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class Task:
|
|
507
|
+
"""One scenario of a WorldSet, as data first: inspect `variant`,
|
|
508
|
+
`instruction`, and `name` without paying for any seeding. `start()`
|
|
509
|
+
materializes the environment; `run()` is start → agent → grade → file."""
|
|
510
|
+
|
|
511
|
+
def __init__(self, world_set: "WorldSet", variant: str) -> None:
|
|
512
|
+
self.world_set = world_set
|
|
513
|
+
self.variant = variant
|
|
514
|
+
task_dir = _pick_task(world_set.bundle_dir, variant)
|
|
515
|
+
self.name = task_dir.name
|
|
516
|
+
instruction_file = task_dir / "instruction.md"
|
|
517
|
+
self.instruction = (
|
|
518
|
+
instruction_file.read_text(encoding="utf-8")
|
|
519
|
+
if instruction_file.is_file()
|
|
520
|
+
else ""
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def start(self) -> TaskEnv:
|
|
524
|
+
return load_path(self.world_set.bundle_dir, variant=self.variant)
|
|
525
|
+
|
|
526
|
+
def run(
|
|
527
|
+
self,
|
|
528
|
+
agent_fn: Callable[[TaskEnv], Any],
|
|
529
|
+
*,
|
|
530
|
+
model: Optional[str] = None,
|
|
531
|
+
file: bool = True,
|
|
532
|
+
session_id: Optional[str] = None,
|
|
533
|
+
) -> Dict[str, Any]:
|
|
534
|
+
env = self.start()
|
|
535
|
+
return env.run(
|
|
536
|
+
agent_fn,
|
|
537
|
+
file=file,
|
|
538
|
+
session_id=session_id,
|
|
539
|
+
# Only override the env's default label when a model was named.
|
|
540
|
+
**({"model": model} if model else {}),
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
def __repr__(self) -> str: # notebook-friendly
|
|
544
|
+
return f"Task({self.variant!r}, name={self.name!r})"
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
class WorldSet:
|
|
548
|
+
"""THE world: one pull, one pinned version, every task. Iterable and
|
|
549
|
+
indexable by variant; `run_all` is the whole suite as one primitive."""
|
|
550
|
+
|
|
551
|
+
def __init__(self, bundle_dir: Path, *, slug: Optional[str],
|
|
552
|
+
branch: Optional[str], version_id: Optional[str]) -> None:
|
|
553
|
+
self.bundle_dir = Path(bundle_dir)
|
|
554
|
+
self.slug = slug
|
|
555
|
+
self.branch = branch
|
|
556
|
+
self.version_id = version_id
|
|
557
|
+
self.tasks: List[Task] = [Task(self, v) for v in variants(self.bundle_dir)]
|
|
558
|
+
|
|
559
|
+
def task(self, variant: str) -> Task:
|
|
560
|
+
for task in self.tasks:
|
|
561
|
+
if task.variant == variant or variant in task.name:
|
|
562
|
+
return task
|
|
563
|
+
known = ", ".join(t.variant for t in self.tasks)
|
|
564
|
+
raise WorldError(f"no task matches {variant!r}; have: {known}")
|
|
565
|
+
|
|
566
|
+
def __iter__(self):
|
|
567
|
+
return iter(self.tasks)
|
|
568
|
+
|
|
569
|
+
def __len__(self) -> int:
|
|
570
|
+
return len(self.tasks)
|
|
571
|
+
|
|
572
|
+
def run_all(
|
|
573
|
+
self,
|
|
574
|
+
agent_fn: Callable[[TaskEnv], Any],
|
|
575
|
+
*,
|
|
576
|
+
model: Optional[str] = None,
|
|
577
|
+
tasks: Optional[List[str]] = None,
|
|
578
|
+
fail_under: Optional[float] = None,
|
|
579
|
+
file: bool = True,
|
|
580
|
+
) -> Dict[str, Any]:
|
|
581
|
+
"""Run the agent across every task (or the named subset), grading and
|
|
582
|
+
filing each. Returns {results: {variant: result}, mean, passed} —
|
|
583
|
+
`passed` is None without a bar, else mean >= fail_under."""
|
|
584
|
+
chosen = (
|
|
585
|
+
[self.task(v) for v in tasks] if tasks is not None else self.tasks
|
|
586
|
+
)
|
|
587
|
+
results: Dict[str, Dict[str, Any]] = {}
|
|
588
|
+
for task in chosen:
|
|
589
|
+
results[task.variant] = task.run(agent_fn, model=model, file=file)
|
|
590
|
+
rewards = [float(r.get("reward") or 0.0) for r in results.values()]
|
|
591
|
+
mean = sum(rewards) / len(rewards) if rewards else 0.0
|
|
592
|
+
return {
|
|
593
|
+
"results": results,
|
|
594
|
+
"mean": mean,
|
|
595
|
+
"passed": None if fail_under is None else mean >= fail_under,
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
def __repr__(self) -> str:
|
|
599
|
+
ref = f"@{self.branch}" if self.branch else ""
|
|
600
|
+
return f"WorldSet({(self.slug or str(self.bundle_dir)) + ref!r}, tasks={len(self.tasks)})"
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def open( # noqa: A001 - deliberate, mirrors pathlib-style opens
|
|
604
|
+
slug_ref: str,
|
|
605
|
+
*,
|
|
606
|
+
host: Optional[str] = None,
|
|
607
|
+
) -> WorldSet:
|
|
608
|
+
"""Open a world from the hub: ONE pull shared by every task.
|
|
609
|
+
|
|
610
|
+
world = worlds.open("google-drive-sim@main")
|
|
611
|
+
world.task("alice").run(my_agent, model="openai/gpt-5.6-terra")
|
|
612
|
+
world.run_all(my_agent, fail_under=0.6)
|
|
613
|
+
"""
|
|
614
|
+
client = GatewayBenchmarkClient.from_env(host)
|
|
615
|
+
target = tempfile.mkdtemp(prefix="gateway-world-pull-")
|
|
616
|
+
pulled = client.pull(slug_ref, target=target)
|
|
617
|
+
bundle = Path(pulled["target"])
|
|
618
|
+
state = local_tree_state(bundle)
|
|
619
|
+
meta = state["meta"] or {}
|
|
620
|
+
return WorldSet(
|
|
621
|
+
bundle,
|
|
622
|
+
slug=meta.get("slug"),
|
|
623
|
+
branch=meta.get("branch") or meta.get("ref"),
|
|
624
|
+
version_id=meta.get("versionId"),
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def open_path(bundle_dir: str | Path) -> WorldSet:
|
|
629
|
+
"""Open a LOCAL checkout as a WorldSet (same filing rules as load_path)."""
|
|
630
|
+
bundle = Path(bundle_dir)
|
|
631
|
+
state = local_tree_state(bundle)
|
|
632
|
+
meta = state["meta"] or {}
|
|
633
|
+
clean = bool(state["clean"]) and bool(meta.get("versionId"))
|
|
634
|
+
return WorldSet(
|
|
635
|
+
bundle,
|
|
636
|
+
slug=meta.get("slug"),
|
|
637
|
+
branch=(meta.get("branch") or meta.get("ref")) if clean else None,
|
|
638
|
+
version_id=meta.get("versionId") if clean else None,
|
|
639
|
+
)
|