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/world_data.py
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""Rows into a platform world, in bulk: the client side of world data batches.
|
|
2
|
+
|
|
3
|
+
The wire is the platform's ``/api/public/worlds/{slug}/data`` routes (the contract,
|
|
4
|
+
section 7): open a batch, register each chunk by digest, PUT the chunk's bytes to
|
|
5
|
+
the presigned URL the platform hands back (or skip it when the platform already
|
|
6
|
+
holds that digest), mark it uploaded, complete the batch, poll it to a terminal
|
|
7
|
+
state. Auth and host resolution match the other clients: the project's public +
|
|
8
|
+
secret key as HTTP Basic (``GATEWAY_PUBLIC_KEY`` / ``GATEWAY_SECRET_KEY`` /
|
|
9
|
+
``GATEWAY_HOST``). Standard library only.
|
|
10
|
+
|
|
11
|
+
:func:`import_rows` is the whole flow the CLI runs: rows streamed from files, cut
|
|
12
|
+
into gzipped chunks one at a time, uploaded ``parallel`` at a time, with a manifest
|
|
13
|
+
at ``.gateway/imports/<batchId>.json`` (section 10) written as each chunk lands so
|
|
14
|
+
``--resume`` can pick the upload up where it stopped.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import base64
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
import tempfile
|
|
24
|
+
import threading
|
|
25
|
+
import time
|
|
26
|
+
import urllib.error
|
|
27
|
+
import urllib.parse
|
|
28
|
+
import urllib.request
|
|
29
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from datetime import datetime, timezone
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
34
|
+
|
|
35
|
+
from gatewaysdk.environment.schema.batch import CHUNK_BYTES, Chunk, chunk_rows, iter_rows, write_report
|
|
36
|
+
|
|
37
|
+
MANIFEST_VERSION = 1
|
|
38
|
+
MANIFEST_DIR = Path(".gateway") / "imports"
|
|
39
|
+
TERMINAL = ("applied", "refused", "published", "failed")
|
|
40
|
+
RESUMABLE = ("open", "uploading")
|
|
41
|
+
POLL_SECONDS = 2.0
|
|
42
|
+
UPLOAD_TIMEOUT = 600
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class WorldDataError(RuntimeError):
|
|
46
|
+
"""Raised when any world data API call fails."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------- the client
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class WorldDataClient:
|
|
53
|
+
"""Talks to a Gateway host's world data batch API."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, host: str, public_key: str, secret_key: str) -> None:
|
|
56
|
+
if not host:
|
|
57
|
+
raise ValueError("host is required (e.g. https://withgateway.ai)")
|
|
58
|
+
if not public_key or not secret_key:
|
|
59
|
+
raise ValueError("both public_key and secret_key are required")
|
|
60
|
+
self.host = host.rstrip("/")
|
|
61
|
+
self._auth = base64.b64encode(f"{public_key}:{secret_key}".encode("utf-8")).decode("ascii")
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_env(cls, host: Optional[str] = None) -> "WorldDataClient":
|
|
65
|
+
return cls(
|
|
66
|
+
host=host or os.environ.get("GATEWAY_HOST", ""),
|
|
67
|
+
public_key=os.environ.get("GATEWAY_PUBLIC_KEY", ""),
|
|
68
|
+
secret_key=os.environ.get("GATEWAY_SECRET_KEY", ""),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def request(self, method: str, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
72
|
+
headers = {"Authorization": f"Basic {self._auth}"}
|
|
73
|
+
data = None
|
|
74
|
+
if body is not None:
|
|
75
|
+
headers["Content-Type"] = "application/json"
|
|
76
|
+
data = json.dumps(body).encode("utf-8")
|
|
77
|
+
req = urllib.request.Request(f"{self.host}{path}", data=data, method=method, headers=headers)
|
|
78
|
+
try:
|
|
79
|
+
with urllib.request.urlopen(req, timeout=120) as resp:
|
|
80
|
+
text = resp.read().decode("utf-8")
|
|
81
|
+
return json.loads(text) if text else {}
|
|
82
|
+
except urllib.error.HTTPError as e:
|
|
83
|
+
detail = e.read().decode("utf-8", "replace")[:500]
|
|
84
|
+
raise WorldDataError(f"{method} {path} -> {e.code}: {detail}") from None
|
|
85
|
+
except urllib.error.URLError as e:
|
|
86
|
+
raise WorldDataError(f"{method} {path} -> {e.reason}") from None
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def route(slug: str) -> str:
|
|
90
|
+
return f"/api/public/worlds/{urllib.parse.quote(slug.split('@', 1)[0], safe='')}/data"
|
|
91
|
+
|
|
92
|
+
def open_batch(self, slug: str, **flags: Any) -> Dict[str, Any]:
|
|
93
|
+
return self.request("POST", f"{self.route(slug)}/batches", {k: v for k, v in flags.items() if v is not None})
|
|
94
|
+
|
|
95
|
+
def register_chunk(self, slug: str, batch_id: str, chunk: Dict[str, Any]) -> Dict[str, Any]:
|
|
96
|
+
return self.request("POST", f"{self.route(slug)}/batches/{batch_id}/chunks", chunk)
|
|
97
|
+
|
|
98
|
+
def mark_uploaded(self, slug: str, batch_id: str, chunk_id: str) -> Dict[str, Any]:
|
|
99
|
+
return self.request("POST", f"{self.route(slug)}/batches/{batch_id}/chunks/{chunk_id}/uploaded")
|
|
100
|
+
|
|
101
|
+
def complete(self, slug: str, batch_id: str) -> Dict[str, Any]:
|
|
102
|
+
return self.request("POST", f"{self.route(slug)}/batches/{batch_id}/complete")
|
|
103
|
+
|
|
104
|
+
def status(self, slug: str, batch_id: str) -> Dict[str, Any]:
|
|
105
|
+
return self.request("GET", f"{self.route(slug)}/batches/{batch_id}")
|
|
106
|
+
|
|
107
|
+
def batches(self, slug: str, *, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
|
|
108
|
+
query = urllib.parse.urlencode({k: v for k, v in (("limit", limit), ("cursor", cursor)) if v is not None})
|
|
109
|
+
return self.request("GET", f"{self.route(slug)}/batches" + (f"?{query}" if query else ""))
|
|
110
|
+
|
|
111
|
+
def publish(self, slug: str, message: Optional[str] = None) -> Dict[str, Any]:
|
|
112
|
+
return self.request("POST", f"{self.route(slug)}/publish", {"message": message} if message else {})
|
|
113
|
+
|
|
114
|
+
def counts(self, slug: str, entity: Optional[str] = None) -> Dict[str, Any]:
|
|
115
|
+
query = f"?entity={urllib.parse.quote(entity, safe='')}" if entity else ""
|
|
116
|
+
return self.request("GET", f"{self.route(slug)}{query}")
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def put_object(url: str, path: Path, size: int, headers: Optional[Dict[str, str]] = None) -> None:
|
|
120
|
+
"""PUT a chunk's bytes to its presigned URL; no auth header, the URL is the grant.
|
|
121
|
+
``headers`` are exactly the ones the platform signed the URL for (content-type,
|
|
122
|
+
content-length, x-amz-checksum-sha256); without them the plain pair is sent."""
|
|
123
|
+
signed = dict(headers) if headers else {"Content-Type": "application/gzip", "Content-Length": str(size)}
|
|
124
|
+
with open(path, "rb") as stream:
|
|
125
|
+
req = urllib.request.Request(url, data=stream, method="PUT", headers=signed)
|
|
126
|
+
try:
|
|
127
|
+
with urllib.request.urlopen(req, timeout=UPLOAD_TIMEOUT) as resp:
|
|
128
|
+
resp.read()
|
|
129
|
+
except urllib.error.HTTPError as e:
|
|
130
|
+
detail = e.read().decode("utf-8", "replace")[:300]
|
|
131
|
+
raise WorldDataError(f"chunk PUT -> {e.code}: {detail}") from None
|
|
132
|
+
except urllib.error.URLError as e:
|
|
133
|
+
raise WorldDataError(f"chunk PUT -> {e.reason}") from None
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def fetch_report(url: str) -> str:
|
|
137
|
+
try:
|
|
138
|
+
with urllib.request.urlopen(urllib.request.Request(url), timeout=120) as resp:
|
|
139
|
+
return resp.read().decode("utf-8")
|
|
140
|
+
except (urllib.error.HTTPError, urllib.error.URLError) as e:
|
|
141
|
+
raise WorldDataError(f"report download failed: {e}") from None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------- the manifest
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _now() -> str:
|
|
148
|
+
return datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass
|
|
152
|
+
class Flags:
|
|
153
|
+
mode: str = "append"
|
|
154
|
+
merge: str = "upsert"
|
|
155
|
+
atomic: bool = True
|
|
156
|
+
publish: str = "now"
|
|
157
|
+
dryRun: bool = False
|
|
158
|
+
chunkBytes: int = CHUNK_BYTES
|
|
159
|
+
chunkRows: Optional[int] = None
|
|
160
|
+
entity: Optional[str] = None
|
|
161
|
+
|
|
162
|
+
def to_json(self) -> Dict[str, Any]:
|
|
163
|
+
return dict(self.__dict__)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Manifest:
|
|
167
|
+
"""``.gateway/imports/<batchId>.json``: what was uploaded, so a run can resume."""
|
|
168
|
+
|
|
169
|
+
def __init__(self, path: Path, data: Dict[str, Any]):
|
|
170
|
+
self.path = path
|
|
171
|
+
self.data = data
|
|
172
|
+
self._lock = threading.Lock()
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def create(cls, base: Path, batch_id: str, slug: str, host: str, flags: Flags, inputs: List[str]) -> "Manifest":
|
|
176
|
+
directory = base / MANIFEST_DIR
|
|
177
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
ignore = base / ".gateway" / ".gitignore"
|
|
179
|
+
if not ignore.exists():
|
|
180
|
+
ignore.write_text("*\n")
|
|
181
|
+
manifest = cls(
|
|
182
|
+
directory / f"{batch_id}.json",
|
|
183
|
+
{
|
|
184
|
+
"version": MANIFEST_VERSION,
|
|
185
|
+
"batchId": batch_id,
|
|
186
|
+
"slug": slug,
|
|
187
|
+
"host": host,
|
|
188
|
+
"flags": flags.to_json(),
|
|
189
|
+
"inputs": list(inputs),
|
|
190
|
+
"chunks": [],
|
|
191
|
+
"completed": False,
|
|
192
|
+
"createdAt": _now(),
|
|
193
|
+
"updatedAt": _now(),
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
manifest.save()
|
|
197
|
+
return manifest
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def load(cls, path: Path) -> "Manifest":
|
|
201
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
202
|
+
if data.get("version") != MANIFEST_VERSION:
|
|
203
|
+
raise WorldDataError(f"{path} is a manifest of version {data.get('version')}, not {MANIFEST_VERSION}")
|
|
204
|
+
return cls(path, data)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def find(cls, base: Path, slug: str, inputs: Optional[List[str]] = None) -> Optional["Manifest"]:
|
|
208
|
+
"""The newest incomplete manifest for ``slug`` (and these inputs when given)."""
|
|
209
|
+
directory = base / MANIFEST_DIR
|
|
210
|
+
if not directory.is_dir():
|
|
211
|
+
return None
|
|
212
|
+
candidates = []
|
|
213
|
+
for path in directory.glob("*.json"):
|
|
214
|
+
try:
|
|
215
|
+
manifest = cls.load(path)
|
|
216
|
+
except (OSError, ValueError, WorldDataError):
|
|
217
|
+
continue
|
|
218
|
+
data = manifest.data
|
|
219
|
+
if data.get("completed") or data.get("slug") != slug.split("@", 1)[0]:
|
|
220
|
+
continue
|
|
221
|
+
if inputs is not None and data.get("inputs") != list(inputs):
|
|
222
|
+
continue
|
|
223
|
+
candidates.append(manifest)
|
|
224
|
+
if not candidates:
|
|
225
|
+
return None
|
|
226
|
+
return max(candidates, key=lambda m: m.data.get("updatedAt", ""))
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def batch_id(self) -> str:
|
|
230
|
+
return self.data["batchId"]
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def flags(self) -> Flags:
|
|
234
|
+
return Flags(**self.data["flags"])
|
|
235
|
+
|
|
236
|
+
def save(self) -> None:
|
|
237
|
+
with self._lock:
|
|
238
|
+
self.data["updatedAt"] = _now()
|
|
239
|
+
tmp = self.path.with_suffix(".json.tmp")
|
|
240
|
+
tmp.write_text(json.dumps(self.data, indent=1) + "\n", encoding="utf-8")
|
|
241
|
+
tmp.replace(self.path)
|
|
242
|
+
|
|
243
|
+
def record(self, entry: Dict[str, Any]) -> None:
|
|
244
|
+
with self._lock:
|
|
245
|
+
chunks = [c for c in self.data["chunks"] if c["ordinal"] != entry["ordinal"]]
|
|
246
|
+
chunks.append(entry)
|
|
247
|
+
self.data["chunks"] = sorted(chunks, key=lambda c: c["ordinal"])
|
|
248
|
+
self.save()
|
|
249
|
+
|
|
250
|
+
def uploaded(self, ordinal: int, digest: str) -> Optional[Dict[str, Any]]:
|
|
251
|
+
for entry in self.data["chunks"]:
|
|
252
|
+
if entry["ordinal"] == ordinal and entry["digest"] == digest and entry.get("uploaded"):
|
|
253
|
+
return entry
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# ---------------------------------------------------------------- the flow
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
Progress = Callable[[str], None]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _quiet(_: str) -> None:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _upload(client: WorldDataClient, slug: str, batch_id: str, chunk: Chunk, manifest: Manifest) -> Dict[str, Any]:
|
|
268
|
+
entry = chunk.manifest_entry()
|
|
269
|
+
registered = client.register_chunk(
|
|
270
|
+
slug,
|
|
271
|
+
batch_id,
|
|
272
|
+
{
|
|
273
|
+
k: v
|
|
274
|
+
for k, v in (
|
|
275
|
+
("ordinal", chunk.ordinal),
|
|
276
|
+
("digest", chunk.digest),
|
|
277
|
+
("bytes", chunk.bytes),
|
|
278
|
+
("rows", chunk.rows),
|
|
279
|
+
("entity", chunk.entity),
|
|
280
|
+
)
|
|
281
|
+
if v is not None
|
|
282
|
+
},
|
|
283
|
+
)
|
|
284
|
+
chunk_id = registered.get("chunkId")
|
|
285
|
+
entry["chunkId"] = chunk_id
|
|
286
|
+
if not registered.get("uploaded"):
|
|
287
|
+
url = registered.get("uploadUrl")
|
|
288
|
+
if not url:
|
|
289
|
+
raise WorldDataError(f"chunk {chunk.ordinal}: the platform gave neither uploaded nor an uploadUrl")
|
|
290
|
+
headers = registered.get("uploadHeaders")
|
|
291
|
+
if headers is not None and not (
|
|
292
|
+
isinstance(headers, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in headers.items())
|
|
293
|
+
):
|
|
294
|
+
raise WorldDataError(f"chunk {chunk.ordinal}: uploadHeaders must map header names to strings")
|
|
295
|
+
client.put_object(url, chunk.path, chunk.bytes, headers)
|
|
296
|
+
client.mark_uploaded(slug, batch_id, chunk_id)
|
|
297
|
+
entry["uploaded"] = True
|
|
298
|
+
manifest.record(entry)
|
|
299
|
+
chunk.path.unlink(missing_ok=True)
|
|
300
|
+
return entry
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def import_rows(
|
|
304
|
+
client: WorldDataClient,
|
|
305
|
+
slug: str,
|
|
306
|
+
inputs: List[str],
|
|
307
|
+
*,
|
|
308
|
+
flags: Flags,
|
|
309
|
+
parallel: int = 4,
|
|
310
|
+
resume: bool = False,
|
|
311
|
+
message: Optional[str] = None,
|
|
312
|
+
base: Optional[Path] = None,
|
|
313
|
+
progress: Progress = _quiet,
|
|
314
|
+
) -> Manifest:
|
|
315
|
+
"""Stream the rows files into chunks, upload them, complete the batch. Returns the
|
|
316
|
+
manifest; the caller polls the batch with :func:`wait`."""
|
|
317
|
+
base = base or Path.cwd()
|
|
318
|
+
manifest: Optional[Manifest] = None
|
|
319
|
+
if resume:
|
|
320
|
+
manifest = Manifest.find(base, slug, inputs)
|
|
321
|
+
if manifest is None:
|
|
322
|
+
raise WorldDataError(f"nothing to resume: no incomplete manifest for {slug} under {base / MANIFEST_DIR}")
|
|
323
|
+
state = client.status(slug, manifest.batch_id).get("state")
|
|
324
|
+
if state not in RESUMABLE:
|
|
325
|
+
raise WorldDataError(f"batch {manifest.batch_id} is {state}; only an open or uploading batch resumes")
|
|
326
|
+
flags = manifest.flags
|
|
327
|
+
progress(f"resuming batch {manifest.batch_id} ({len(manifest.data['chunks'])} chunks recorded)")
|
|
328
|
+
else:
|
|
329
|
+
opened = client.open_batch(
|
|
330
|
+
slug,
|
|
331
|
+
mode=flags.mode,
|
|
332
|
+
merge=flags.merge,
|
|
333
|
+
atomic=flags.atomic,
|
|
334
|
+
dryRun=flags.dryRun,
|
|
335
|
+
publish=flags.publish,
|
|
336
|
+
message=message,
|
|
337
|
+
)
|
|
338
|
+
batch_id = opened.get("batchId")
|
|
339
|
+
if not isinstance(batch_id, str) or not batch_id:
|
|
340
|
+
raise WorldDataError("the platform opened no batch (no batchId in the answer)")
|
|
341
|
+
limits = opened.get("limits") or {}
|
|
342
|
+
if isinstance(limits.get("chunkBytes"), int) and flags.chunkBytes > limits["chunkBytes"]:
|
|
343
|
+
raise WorldDataError(
|
|
344
|
+
f"--chunk-bytes {flags.chunkBytes} exceeds the platform's chunk limit {limits['chunkBytes']}"
|
|
345
|
+
)
|
|
346
|
+
manifest = Manifest.create(base, batch_id, slug.split("@", 1)[0], client.host, flags, inputs)
|
|
347
|
+
progress(f"batch {batch_id} open")
|
|
348
|
+
assert manifest is not None
|
|
349
|
+
batch_id = manifest.batch_id
|
|
350
|
+
errors: List[BaseException] = []
|
|
351
|
+
with tempfile.TemporaryDirectory(prefix="gateway-chunks-") as workdir:
|
|
352
|
+
with ThreadPoolExecutor(max_workers=max(1, parallel)) as pool:
|
|
353
|
+
pending = []
|
|
354
|
+
rows = iter_rows(inputs, entity=flags.entity)
|
|
355
|
+
for chunk in chunk_rows(
|
|
356
|
+
rows, workdir, chunk_bytes=flags.chunkBytes, chunk_rows=flags.chunkRows, entity=flags.entity
|
|
357
|
+
):
|
|
358
|
+
if errors:
|
|
359
|
+
break
|
|
360
|
+
known = manifest.uploaded(chunk.ordinal, chunk.digest)
|
|
361
|
+
if known is not None:
|
|
362
|
+
progress(f"chunk {chunk.ordinal}: already uploaded ({chunk.rows} rows)")
|
|
363
|
+
chunk.path.unlink(missing_ok=True)
|
|
364
|
+
continue
|
|
365
|
+
|
|
366
|
+
def job(chunk: Chunk = chunk) -> None:
|
|
367
|
+
try:
|
|
368
|
+
entry = _upload(client, slug, batch_id, chunk, manifest)
|
|
369
|
+
progress(
|
|
370
|
+
f"chunk {chunk.ordinal}: uploaded {chunk.rows} rows, {chunk.bytes} bytes ({entry['chunkId']})"
|
|
371
|
+
)
|
|
372
|
+
except BaseException as error: # surfaced after the pool drains
|
|
373
|
+
errors.append(error)
|
|
374
|
+
|
|
375
|
+
pending.append(pool.submit(job))
|
|
376
|
+
# Bound the chunks on disk to the workers: the producer waits for a slot.
|
|
377
|
+
while sum(1 for f in pending if not f.done()) >= max(1, parallel):
|
|
378
|
+
time.sleep(0.05)
|
|
379
|
+
for future in pending:
|
|
380
|
+
future.result()
|
|
381
|
+
if errors:
|
|
382
|
+
raise errors[0]
|
|
383
|
+
client.complete(slug, batch_id)
|
|
384
|
+
manifest.data["completed"] = True
|
|
385
|
+
manifest.save()
|
|
386
|
+
progress(f"batch {batch_id} complete: {len(manifest.data['chunks'])} chunks")
|
|
387
|
+
return manifest
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def wait(
|
|
391
|
+
client: WorldDataClient,
|
|
392
|
+
slug: str,
|
|
393
|
+
batch_id: str,
|
|
394
|
+
*,
|
|
395
|
+
progress: Progress = _quiet,
|
|
396
|
+
poll: float = POLL_SECONDS,
|
|
397
|
+
timeout: Optional[float] = None,
|
|
398
|
+
) -> Dict[str, Any]:
|
|
399
|
+
"""Poll the batch until a terminal state, one progress line per phase change."""
|
|
400
|
+
started = time.monotonic()
|
|
401
|
+
last = None
|
|
402
|
+
while True:
|
|
403
|
+
status = client.status(slug, batch_id)
|
|
404
|
+
phase = (status.get("state"), (status.get("progress") or {}).get("phase"))
|
|
405
|
+
if phase != last:
|
|
406
|
+
last = phase
|
|
407
|
+
detail = status.get("progress") or {}
|
|
408
|
+
progress(f"{status.get('state')}" + (f" ({detail.get('phase')})" if detail.get("phase") else ""))
|
|
409
|
+
if status.get("state") in TERMINAL:
|
|
410
|
+
return status
|
|
411
|
+
if timeout is not None and time.monotonic() - started > timeout:
|
|
412
|
+
return status
|
|
413
|
+
time.sleep(poll)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def write_status_report(client: WorldDataClient, status: Dict[str, Any], destination: Path) -> int:
|
|
417
|
+
"""The batch's refusals as a JSONL report: the full report when the platform
|
|
418
|
+
offers one, else the records the status carries."""
|
|
419
|
+
url = status.get("reportUrl")
|
|
420
|
+
if url:
|
|
421
|
+
destination.write_text(client.fetch_report(url), encoding="utf-8")
|
|
422
|
+
return int(status.get("refusalCount") or 0)
|
|
423
|
+
records: Iterable[dict] = status.get("refusals") or []
|
|
424
|
+
with open(destination, "w", encoding="utf-8") as out:
|
|
425
|
+
return write_report(records, out)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def tty_progress(stream: Any = None) -> Progress:
|
|
429
|
+
"""Progress lines on a TTY; silence otherwise."""
|
|
430
|
+
stream = stream or sys.stderr
|
|
431
|
+
if not hasattr(stream, "isatty") or not stream.isatty():
|
|
432
|
+
return _quiet
|
|
433
|
+
|
|
434
|
+
def show(line: str) -> None:
|
|
435
|
+
print(line, file=stream, flush=True)
|
|
436
|
+
|
|
437
|
+
return show
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
__all__ = [
|
|
441
|
+
"Flags",
|
|
442
|
+
"MANIFEST_DIR",
|
|
443
|
+
"MANIFEST_VERSION",
|
|
444
|
+
"Manifest",
|
|
445
|
+
"RESUMABLE",
|
|
446
|
+
"TERMINAL",
|
|
447
|
+
"WorldDataClient",
|
|
448
|
+
"WorldDataError",
|
|
449
|
+
"import_rows",
|
|
450
|
+
"tty_progress",
|
|
451
|
+
"wait",
|
|
452
|
+
"write_status_report",
|
|
453
|
+
]
|