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,111 @@
|
|
|
1
|
+
"""Automations client for Gateway public REST API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List, Mapping, MutableMapping, Optional
|
|
6
|
+
from urllib import parse
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AutomationClientError(RuntimeError):
|
|
12
|
+
"""Raised when the Gateway automations API returns an error response."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, message: str, *, status_code: Optional[int] = None, body: Optional[str] = None) -> None:
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.body = body
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AutomationClient:
|
|
21
|
+
"""HTTP client for project-scoped automations endpoints (Basic auth)."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, base_url: str, public_key: str, secret_key: str, *, timeout_seconds: float = 60.0) -> None:
|
|
24
|
+
self._base = base_url.rstrip("/")
|
|
25
|
+
self._timeout = timeout_seconds
|
|
26
|
+
self._session = requests.Session()
|
|
27
|
+
self._session.auth = (public_key, secret_key)
|
|
28
|
+
self._session.headers.update({"Accept": "application/json"})
|
|
29
|
+
|
|
30
|
+
def _request(
|
|
31
|
+
self,
|
|
32
|
+
method: str,
|
|
33
|
+
path: str,
|
|
34
|
+
*,
|
|
35
|
+
query: Optional[Mapping[str, Any]] = None,
|
|
36
|
+
json_body: Optional[MutableMapping[str, Any]] = None,
|
|
37
|
+
) -> Any:
|
|
38
|
+
url = f"{self._base}{path}"
|
|
39
|
+
params = {k: v for k, v in (query or {}).items() if v is not None}
|
|
40
|
+
try:
|
|
41
|
+
resp = self._session.request(
|
|
42
|
+
method,
|
|
43
|
+
url,
|
|
44
|
+
params=params or None,
|
|
45
|
+
json=json_body,
|
|
46
|
+
timeout=self._timeout,
|
|
47
|
+
)
|
|
48
|
+
except requests.RequestException as exc:
|
|
49
|
+
raise AutomationClientError(f"Automations HTTP request failed for {path}: {exc}") from exc
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
out = resp.json() if resp.content else {}
|
|
53
|
+
except ValueError:
|
|
54
|
+
out = {}
|
|
55
|
+
|
|
56
|
+
if not resp.ok:
|
|
57
|
+
msg = str(out.get("message", resp.reason or resp.text))
|
|
58
|
+
raise AutomationClientError(
|
|
59
|
+
f"Gateway automations API error {resp.status_code} on {path}: {msg}",
|
|
60
|
+
status_code=resp.status_code,
|
|
61
|
+
body=resp.text,
|
|
62
|
+
)
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
def list(self) -> List[Dict[str, Any]]:
|
|
66
|
+
"""GET /api/public/automations — list automations for the authenticated project."""
|
|
67
|
+
out = self._request("GET", "/api/public/automations")
|
|
68
|
+
if not isinstance(out, dict):
|
|
69
|
+
raise AutomationClientError(f"Unexpected list() response shape: {type(out).__name__}")
|
|
70
|
+
data = out.get("automations")
|
|
71
|
+
if not isinstance(data, list):
|
|
72
|
+
raise AutomationClientError("Response missing 'automations' array")
|
|
73
|
+
return [dict(x) for x in data if isinstance(x, dict)]
|
|
74
|
+
|
|
75
|
+
def create(
|
|
76
|
+
self,
|
|
77
|
+
name: str,
|
|
78
|
+
trigger_config: Mapping[str, Any],
|
|
79
|
+
action_config: Mapping[str, Any],
|
|
80
|
+
) -> Dict[str, Any]:
|
|
81
|
+
"""POST /api/public/automations — create trigger, action, and automation."""
|
|
82
|
+
body = {"name": name, "trigger": dict(trigger_config), "action": dict(action_config)}
|
|
83
|
+
out = self._request("POST", "/api/public/automations", json_body=body)
|
|
84
|
+
if not isinstance(out, dict):
|
|
85
|
+
raise AutomationClientError(f"Unexpected create() response shape: {type(out).__name__}")
|
|
86
|
+
return dict(out)
|
|
87
|
+
|
|
88
|
+
def trigger(self, automation_id: str, payload: Optional[Mapping[str, Any]] = None) -> Dict[str, Any]:
|
|
89
|
+
"""POST /api/public/automations/{id}/trigger — enqueue a manual run; optional JSON body becomes execution input."""
|
|
90
|
+
path = f"/api/public/automations/{parse.quote(automation_id, safe='')}/trigger"
|
|
91
|
+
out = self._request("POST", path, json_body=dict(payload) if payload is not None else {})
|
|
92
|
+
if not isinstance(out, dict):
|
|
93
|
+
raise AutomationClientError(f"Unexpected trigger() response shape: {type(out).__name__}")
|
|
94
|
+
return dict(out)
|
|
95
|
+
|
|
96
|
+
def get_executions(
|
|
97
|
+
self,
|
|
98
|
+
automation_id: str,
|
|
99
|
+
*,
|
|
100
|
+
limit: int = 50,
|
|
101
|
+
offset: int = 0,
|
|
102
|
+
) -> List[Dict[str, Any]]:
|
|
103
|
+
"""GET /api/public/automations/{id}/executions — paginated execution history."""
|
|
104
|
+
path = f"/api/public/automations/{parse.quote(automation_id, safe='')}/executions"
|
|
105
|
+
out = self._request("GET", path, query={"limit": limit, "offset": offset})
|
|
106
|
+
if not isinstance(out, dict):
|
|
107
|
+
raise AutomationClientError(f"Unexpected get_executions() response shape: {type(out).__name__}")
|
|
108
|
+
data = out.get("executions")
|
|
109
|
+
if not isinstance(data, list):
|
|
110
|
+
raise AutomationClientError("Response missing 'executions' array")
|
|
111
|
+
return [dict(x) for x in data if isinstance(x, dict)]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# benchmark_hub — Gateway benchmark container hub SDK
|
|
2
|
+
|
|
3
|
+
Push, version, browse, and evaluate benchmark **containers** (verifiers-format
|
|
4
|
+
environments) on Gateway. This is Prime Intellect's hub model ported onto the
|
|
5
|
+
Gateway stack: the **source bundle is the versioned, browsable source of truth**;
|
|
6
|
+
an OCI image is built per version for execution.
|
|
7
|
+
|
|
8
|
+
Everything here talks **only to a Gateway host** — never to `app.primeintellect.ai`.
|
|
9
|
+
|
|
10
|
+
## Auth + config (env)
|
|
11
|
+
|
|
12
|
+
| Env var | Purpose |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `GATEWAY_HOST` | Gateway base URL, e.g. `https://withgateway.ai` |
|
|
15
|
+
| `GATEWAY_PUBLIC_KEY` / `GATEWAY_SECRET_KEY` | project API key pair — **writes require the secret key** (Basic auth) |
|
|
16
|
+
|
|
17
|
+
Bundle storage is flexible/enterprise-overridable server-side via
|
|
18
|
+
`LANGFUSE_S3_BENCHMARK_UPLOAD_*` (falls back to the media-upload bucket under a
|
|
19
|
+
`benchmark-containers/` prefix — zero-config by default).
|
|
20
|
+
|
|
21
|
+
## Push a container
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from gatewaysdk.benchmark_hub import GatewayBenchmarkClient
|
|
25
|
+
|
|
26
|
+
client = GatewayBenchmarkClient.from_env() # GATEWAY_HOST/PUBLIC_KEY/SECRET_KEY
|
|
27
|
+
result = client.push("./automationbench", visibility="PUBLIC")
|
|
28
|
+
# resolve -> versions (presigned PUT) -> finalize
|
|
29
|
+
print(result) # {containerId, versionId, contentHash, state, slug, alreadyUploaded}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
CLI: `python -m gatewaysdk.benchmark_hub.client push ./automationbench --visibility PUBLIC`
|
|
33
|
+
|
|
34
|
+
The push computes a **source-only `content_hash`** (vendored byte-compatible with
|
|
35
|
+
Prime's hub) as the logical version id, builds a deterministic source tarball,
|
|
36
|
+
and sends the tarball's real sha256 as the S3 checksum (distinct from the content
|
|
37
|
+
hash — this is what makes the presigned upload succeed). The **full file tree** is
|
|
38
|
+
recorded separately for the browser.
|
|
39
|
+
|
|
40
|
+
## Harvest a version-pinned evaluation
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from gatewaysdk.benchmark_hub import GatewayEvalsClient
|
|
44
|
+
|
|
45
|
+
evals = GatewayEvalsClient.from_env()
|
|
46
|
+
eval_id = evals.create_evaluation(version_id, model="gpt-4o-mini")
|
|
47
|
+
evals.push_samples(eval_id, samples) # batched; multi-reward per sample
|
|
48
|
+
evals.finalize_evaluation(eval_id, metrics={"pass_rate": 0.87}, cost={"total_usd": 0.42})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
An evaluation is **pinned to the immutable container version**, so scores stay
|
|
52
|
+
reproducible and diffable across re-pushes. Each sample carries the overall score
|
|
53
|
+
**and** the named sub-reward breakdown (`rewards`), never just an average.
|
|
54
|
+
`normalize_sample` accepts prime-evals snake_case (`example_id`, `rollout_index`)
|
|
55
|
+
so a verifiers/prime `Sample` harvests unchanged.
|
|
56
|
+
|
|
57
|
+
## Server API (under `/api/public/benchmark-containers`)
|
|
58
|
+
|
|
59
|
+
| Route | Auth | Purpose |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `POST /resolve` | write | get-or-create a container |
|
|
62
|
+
| `POST /versions` | write | create/dedup a version + presigned upload |
|
|
63
|
+
| `POST /versions/{id}/finalize` | write | mark the source READY |
|
|
64
|
+
| `GET /` | read | list containers + versions |
|
|
65
|
+
| `GET /versions/{id}/files` | read | full file tree (browser) |
|
|
66
|
+
| `GET /versions/{id}/download` | read | signed URL to the source tarball |
|
|
67
|
+
| `POST /evaluations` | write | create a version-pinned evaluation |
|
|
68
|
+
| `POST /evaluations/{id}/samples` | write | append multi-reward sample batches |
|
|
69
|
+
| `POST /evaluations/{id}/finalize` | write | metrics + cost + status |
|
|
70
|
+
|
|
71
|
+
## What's vendored vs ours (per the integration strategy)
|
|
72
|
+
|
|
73
|
+
- **Vendored (MIT, Prime):** the four content-hash helpers in `_archive.py` —
|
|
74
|
+
byte-compatible hashing for hub interop. There is no library to wrap; the rest
|
|
75
|
+
of Prime's hub is a CLI welded to Typer/Rich.
|
|
76
|
+
- **Ours:** the hub backend (schema, routes, storage), the reimplemented push +
|
|
77
|
+
eval clients (Prime's `prime-sandboxes`/`prime env`/hosted eval all terminate
|
|
78
|
+
at `api.primeintellect.ai` and aren't self-hostable).
|
|
79
|
+
- **Borrowed shape:** the eval client mirrors prime-evals' create→push→finalize,
|
|
80
|
+
harvesting into Gateway's own store.
|
|
81
|
+
|
|
82
|
+
## Not yet wired
|
|
83
|
+
|
|
84
|
+
- **`verifiers` scoring** — for verifiers-format containers, `Environment.evaluate()`
|
|
85
|
+
produces the sample rows for free (the one genuinely reusable open Prime lib);
|
|
86
|
+
the worker that runs a finalized container should feed the eval routes from it.
|
|
87
|
+
- **Per-version OCI image build** — `content_hash`-tagged image so the runner can
|
|
88
|
+
execute the container (`generic --image`); `image_ref` stays null until then.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Gateway benchmark-container hub SDK.
|
|
2
|
+
|
|
3
|
+
Client-side helpers for packaging a benchmark "container" (a verifiers-format
|
|
4
|
+
environment: ``pyproject.toml`` + source modules + README) and pushing it to the
|
|
5
|
+
Gateway hub for content-addressed version control.
|
|
6
|
+
|
|
7
|
+
The content-hash helpers in :mod:`._archive` are vendored verbatim from Prime
|
|
8
|
+
Intellect's ``prime`` CLI (MIT) so our ``content_hash`` is byte-compatible with
|
|
9
|
+
theirs — see that module's header for attribution.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from ._archive import collect_archive_files, compute_content_hash
|
|
13
|
+
from ._tar import safe_tar_extract
|
|
14
|
+
from .client import (
|
|
15
|
+
BenchmarkPushError,
|
|
16
|
+
DEFAULT_BRANCH,
|
|
17
|
+
GatewayBenchmarkClient,
|
|
18
|
+
build_file_manifest,
|
|
19
|
+
build_source_tarball,
|
|
20
|
+
build_wheel,
|
|
21
|
+
normalize_package_name,
|
|
22
|
+
parse_wheel_requires_dist,
|
|
23
|
+
local_tree_state,
|
|
24
|
+
read_local_meta,
|
|
25
|
+
read_pyproject,
|
|
26
|
+
sha256_base64,
|
|
27
|
+
write_local_meta,
|
|
28
|
+
)
|
|
29
|
+
from .eval_config import EvalConfig
|
|
30
|
+
from .evals import (
|
|
31
|
+
EvalHarvestError,
|
|
32
|
+
GatewayEvalsClient,
|
|
33
|
+
normalize_sample,
|
|
34
|
+
)
|
|
35
|
+
from .path_utils import (
|
|
36
|
+
count_saved_rollouts,
|
|
37
|
+
find_latest_incomplete_eval_results_path,
|
|
38
|
+
get_results_path,
|
|
39
|
+
is_valid_eval_results_path,
|
|
40
|
+
)
|
|
41
|
+
from .save_utils import (
|
|
42
|
+
load_metadata,
|
|
43
|
+
load_outputs,
|
|
44
|
+
save_metadata,
|
|
45
|
+
save_outputs,
|
|
46
|
+
validate_resume_metadata,
|
|
47
|
+
)
|
|
48
|
+
from .harbor_adapter import (
|
|
49
|
+
detect_env_kind,
|
|
50
|
+
find_mcp_config,
|
|
51
|
+
harvest_job_dir,
|
|
52
|
+
resolve_harbor_model,
|
|
53
|
+
run_harbor_job,
|
|
54
|
+
samples_from_job_dir,
|
|
55
|
+
tasks_root,
|
|
56
|
+
)
|
|
57
|
+
from .verifiers_adapter import (
|
|
58
|
+
metrics_from_generate_outputs,
|
|
59
|
+
run_and_harvest,
|
|
60
|
+
run_local_eval,
|
|
61
|
+
samples_from_generate_outputs,
|
|
62
|
+
score_environment,
|
|
63
|
+
write_results_artifact,
|
|
64
|
+
)
|
|
65
|
+
from .versioning import (
|
|
66
|
+
bump_post_version,
|
|
67
|
+
bump_rc_version,
|
|
68
|
+
bump_version,
|
|
69
|
+
update_pyproject_version,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
"compute_content_hash",
|
|
74
|
+
"collect_archive_files",
|
|
75
|
+
"safe_tar_extract",
|
|
76
|
+
"bump_version",
|
|
77
|
+
"bump_rc_version",
|
|
78
|
+
"bump_post_version",
|
|
79
|
+
"update_pyproject_version",
|
|
80
|
+
"GatewayBenchmarkClient",
|
|
81
|
+
"DEFAULT_BRANCH",
|
|
82
|
+
"local_tree_state",
|
|
83
|
+
"read_local_meta",
|
|
84
|
+
"write_local_meta",
|
|
85
|
+
"BenchmarkPushError",
|
|
86
|
+
"build_source_tarball",
|
|
87
|
+
"build_file_manifest",
|
|
88
|
+
"build_wheel",
|
|
89
|
+
"parse_wheel_requires_dist",
|
|
90
|
+
"normalize_package_name",
|
|
91
|
+
"sha256_base64",
|
|
92
|
+
"read_pyproject",
|
|
93
|
+
"GatewayEvalsClient",
|
|
94
|
+
"EvalHarvestError",
|
|
95
|
+
"normalize_sample",
|
|
96
|
+
"samples_from_generate_outputs",
|
|
97
|
+
"metrics_from_generate_outputs",
|
|
98
|
+
"score_environment",
|
|
99
|
+
"run_and_harvest",
|
|
100
|
+
"run_local_eval",
|
|
101
|
+
"write_results_artifact",
|
|
102
|
+
"detect_env_kind",
|
|
103
|
+
"tasks_root",
|
|
104
|
+
"run_harbor_job",
|
|
105
|
+
"harvest_job_dir",
|
|
106
|
+
"samples_from_job_dir",
|
|
107
|
+
"find_mcp_config",
|
|
108
|
+
"resolve_harbor_model",
|
|
109
|
+
"EvalConfig",
|
|
110
|
+
"save_outputs",
|
|
111
|
+
"load_outputs",
|
|
112
|
+
"save_metadata",
|
|
113
|
+
"load_metadata",
|
|
114
|
+
"validate_resume_metadata",
|
|
115
|
+
"get_results_path",
|
|
116
|
+
"is_valid_eval_results_path",
|
|
117
|
+
"count_saved_rollouts",
|
|
118
|
+
"find_latest_incomplete_eval_results_path",
|
|
119
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Deterministic content hashing for benchmark-container source bundles.
|
|
2
|
+
|
|
3
|
+
Vendored verbatim (with only docstring/typing cosmetics) from Prime Intellect's
|
|
4
|
+
``prime`` CLI so that the ``content_hash`` we compute for a container is
|
|
5
|
+
byte-for-byte identical to the one their Environments Hub computes for the same
|
|
6
|
+
files. That byte-compatibility is the whole point of copying rather than
|
|
7
|
+
reimplementing: a container hashed here and on hub.primeintellect.ai resolves to
|
|
8
|
+
the same logical version id.
|
|
9
|
+
|
|
10
|
+
Source: https://github.com/PrimeIntellect-ai/prime
|
|
11
|
+
packages/prime/src/prime_cli/commands/env.py (functions at L575-L689)
|
|
12
|
+
License: MIT (c) Prime Intellect. See THIRD_PARTY_LICENSES for the full text.
|
|
13
|
+
|
|
14
|
+
IMPORTANT: this is the *source-only* collection. Prime-compatible bundles hash
|
|
15
|
+
only ``README.md``, ``pyproject.toml`` and ``*.py`` at the root (plus nested
|
|
16
|
+
package dirs). Gateway-only bundles hash every non-hidden root file as well:
|
|
17
|
+
mock-tools bundles (root ``gateway-env.toml``) carry their executable contract
|
|
18
|
+
in root ``tasks.jsonl`` and ``tool-schemas.json``, and api worlds (root
|
|
19
|
+
``gateway-api-world.toml``) carry theirs in the marker itself plus root
|
|
20
|
+
``openapi.json`` — omitting any of these produced a READY bundle the platform
|
|
21
|
+
could not detect or execute. Dotfiles, ``__pycache__``, build outputs and
|
|
22
|
+
``.gitignore``-d paths are still dropped.
|
|
23
|
+
The hash is the version primary key. The *full* file tree shown in the
|
|
24
|
+
GitHub-style browser is collected and stored separately and must NOT feed this
|
|
25
|
+
function, or hashes would diverge from Prime's.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import hashlib
|
|
29
|
+
import os
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Callable, Dict, List, Optional
|
|
32
|
+
|
|
33
|
+
try: # optional: only needed when a container actually ships a .gitignore
|
|
34
|
+
from gitignore_parser import parse_gitignore
|
|
35
|
+
except ImportError: # pragma: no cover - exercised only when the dep is absent
|
|
36
|
+
parse_gitignore = None # type: ignore[assignment]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def should_include_file_in_archive(file_path: Path, base_path: Path) -> bool:
|
|
40
|
+
"""Return True if ``file_path`` belongs in the hashed source archive."""
|
|
41
|
+
if not file_path.is_file():
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
# Skip symlinks - they cause extraction failures on the server side.
|
|
45
|
+
if file_path.is_symlink():
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
rel_path = file_path.relative_to(base_path)
|
|
49
|
+
|
|
50
|
+
# Skip hidden files.
|
|
51
|
+
if file_path.name.startswith("."):
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
# Skip files in __pycache__ directories.
|
|
55
|
+
if "__pycache__" in rel_path.parts:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def should_include_directory_in_archive(dir_path: Path) -> bool:
|
|
62
|
+
"""Return True if ``dir_path`` should be traversed into the archive."""
|
|
63
|
+
if not dir_path.is_dir():
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
# Skip hidden directories (includes .prime/, .git/, etc.).
|
|
67
|
+
if dir_path.name.startswith("."):
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
# Skip build artifacts, cache directories, and outputs.
|
|
71
|
+
if dir_path.name in ["dist", "__pycache__", "build", "outputs"]:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
# Skip egg-info directories.
|
|
75
|
+
if dir_path.name.endswith(".egg-info"):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _build_gitignore_matcher(env_path: Path) -> Optional[Callable[[str], bool]]:
|
|
82
|
+
"""Build a matcher for the root .gitignore file, if present."""
|
|
83
|
+
gitignore_path = env_path / ".gitignore"
|
|
84
|
+
if not gitignore_path.exists():
|
|
85
|
+
return None
|
|
86
|
+
if parse_gitignore is None:
|
|
87
|
+
# Explicit failure rather than silently producing a hash that would
|
|
88
|
+
# diverge from Prime's by including .gitignore-d files.
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
"A .gitignore is present but 'gitignore_parser' is not installed. "
|
|
91
|
+
"Install it (it is a declared dependency of gatewaysdk) so the "
|
|
92
|
+
"content hash stays compatible with the hub."
|
|
93
|
+
)
|
|
94
|
+
return parse_gitignore(str(gitignore_path), base_dir=str(env_path))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def collect_archive_files(env_path: Path) -> List[Path]:
|
|
98
|
+
"""Collect archive file paths in deterministic order, honoring .gitignore."""
|
|
99
|
+
env_path = Path(env_path) # accept str paths (CLI passes a string)
|
|
100
|
+
ignore_matcher = _build_gitignore_matcher(env_path)
|
|
101
|
+
files_by_rel_path: Dict[str, Path] = {}
|
|
102
|
+
|
|
103
|
+
def maybe_add_file(file_path: Path) -> None:
|
|
104
|
+
if not should_include_file_in_archive(file_path, env_path):
|
|
105
|
+
return
|
|
106
|
+
if ignore_matcher is not None and ignore_matcher(str(file_path)):
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
rel_path = str(file_path.relative_to(env_path)).replace("\\", "/")
|
|
110
|
+
files_by_rel_path[rel_path] = file_path
|
|
111
|
+
|
|
112
|
+
# Prime bundles keep Prime's root globs. A Gateway-only root marker widens
|
|
113
|
+
# them to every ordinary root file, because those kinds keep executable
|
|
114
|
+
# contract at the root: mock-tools (``gateway-env.toml``) in ``tasks.jsonl``
|
|
115
|
+
# / ``tool-schemas.json``, api worlds (``gateway-api-world.toml``) in the
|
|
116
|
+
# marker itself and ``openapi.json``. Include them so the archive and its
|
|
117
|
+
# content hash describe the complete runnable source rather than a
|
|
118
|
+
# READY-but-broken subset — an api world whose marker never reached the
|
|
119
|
+
# bundle was detected as harbor and surfaced zero tools.
|
|
120
|
+
#
|
|
121
|
+
# The manifest MUST be hashed or a manifest-only edit — changing the served
|
|
122
|
+
# tool list, the kind, the db, or the dependency pins — produces an identical
|
|
123
|
+
# hash, dedups into the previous version, and is silently discarded while
|
|
124
|
+
# reporting success. That made every such edit a no-op.
|
|
125
|
+
#
|
|
126
|
+
# This does not break Prime compatibility: a Prime-compatible bundle has no
|
|
127
|
+
# Gateway marker, so it continues to use the original globs unchanged.
|
|
128
|
+
root_patterns = ["README.md", "pyproject.toml", "*.py"]
|
|
129
|
+
if (env_path / "gateway-env.toml").is_file() or (
|
|
130
|
+
env_path / "gateway-api-world.toml"
|
|
131
|
+
).is_file():
|
|
132
|
+
root_patterns.append("*")
|
|
133
|
+
for pattern in root_patterns:
|
|
134
|
+
for file_path in sorted(env_path.glob(pattern), key=lambda p: p.name):
|
|
135
|
+
maybe_add_file(file_path)
|
|
136
|
+
|
|
137
|
+
def is_nested_dir_ignored(dir_path: Path) -> bool:
|
|
138
|
+
"""Check if a nested directory should be pruned from traversal."""
|
|
139
|
+
if not should_include_directory_in_archive(dir_path):
|
|
140
|
+
return True
|
|
141
|
+
if ignore_matcher is not None and ignore_matcher(str(dir_path)):
|
|
142
|
+
return True
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
for subdir in sorted(env_path.iterdir(), key=lambda path: path.name):
|
|
146
|
+
if not should_include_directory_in_archive(subdir):
|
|
147
|
+
continue
|
|
148
|
+
if is_nested_dir_ignored(subdir):
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
for root, dirnames, filenames in os.walk(subdir):
|
|
152
|
+
root_path = Path(root)
|
|
153
|
+
dirnames[:] = sorted(
|
|
154
|
+
dirname for dirname in dirnames if not is_nested_dir_ignored(root_path / dirname)
|
|
155
|
+
)
|
|
156
|
+
for filename in sorted(filenames):
|
|
157
|
+
maybe_add_file(root_path / filename)
|
|
158
|
+
|
|
159
|
+
return [files_by_rel_path[rel_path] for rel_path in sorted(files_by_rel_path)]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def compute_content_hash(env_path: Path) -> str:
|
|
163
|
+
"""Compute a deterministic, cross-platform content hash for a container.
|
|
164
|
+
|
|
165
|
+
SHA-256 over each included file's normalized relative path (prefixed with
|
|
166
|
+
``file:``) followed by its raw bytes, in sorted path order. This is a
|
|
167
|
+
*logical* digest of the source set — NOT the sha256 of any tarball object,
|
|
168
|
+
so it cannot double as an S3 upload checksum.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
env_path: Path to the container directory.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
SHA-256 hexdigest of the container content.
|
|
175
|
+
"""
|
|
176
|
+
env_path = Path(env_path) # accept str paths (CLI passes a string)
|
|
177
|
+
content_hasher = hashlib.sha256()
|
|
178
|
+
|
|
179
|
+
for file_path in collect_archive_files(env_path):
|
|
180
|
+
normalized_path = str(file_path.relative_to(env_path)).replace("\\", "/")
|
|
181
|
+
content_hasher.update(f"file:{normalized_path}".encode("utf-8"))
|
|
182
|
+
try:
|
|
183
|
+
with open(file_path, "rb") as f:
|
|
184
|
+
content_hasher.update(f.read())
|
|
185
|
+
except IOError:
|
|
186
|
+
# Skip files that can't be read.
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
return content_hasher.hexdigest()
|