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.
Files changed (376) hide show
  1. gatewaysdk/__init__.py +540 -0
  2. gatewaysdk/adapter/__init__.py +15 -0
  3. gatewaysdk/adapter/base.py +94 -0
  4. gatewaysdk/adapter/messages.py +270 -0
  5. gatewaysdk/adapter/triplet.py +1026 -0
  6. gatewaysdk/algorithm/__init__.py +39 -0
  7. gatewaysdk/algorithm/apo/__init__.py +5 -0
  8. gatewaysdk/algorithm/apo/apo.py +898 -0
  9. gatewaysdk/algorithm/apo/prompts/apply_edit_variant01.poml +22 -0
  10. gatewaysdk/algorithm/apo/prompts/apply_edit_variant02.poml +18 -0
  11. gatewaysdk/algorithm/apo/prompts/text_gradient_variant01.poml +18 -0
  12. gatewaysdk/algorithm/apo/prompts/text_gradient_variant02.poml +16 -0
  13. gatewaysdk/algorithm/apo/prompts/text_gradient_variant03.poml +107 -0
  14. gatewaysdk/algorithm/base.py +262 -0
  15. gatewaysdk/algorithm/decorator.py +264 -0
  16. gatewaysdk/algorithm/evals/__init__.py +7 -0
  17. gatewaysdk/algorithm/evals/evals.py +217 -0
  18. gatewaysdk/algorithm/fast.py +250 -0
  19. gatewaysdk/algorithm/gepa/__init__.py +61 -0
  20. gatewaysdk/algorithm/gepa/adapter.py +495 -0
  21. gatewaysdk/algorithm/gepa/gepa.py +570 -0
  22. gatewaysdk/algorithm/gepa/lib/__init__.py +18 -0
  23. gatewaysdk/algorithm/gepa/lib/adapters/README.md +12 -0
  24. gatewaysdk/algorithm/gepa/lib/adapters/__init__.py +0 -0
  25. gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/README.md +341 -0
  26. gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/__init__.py +1 -0
  27. gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
  28. gatewaysdk/algorithm/gepa/lib/adapters/anymaths_adapter/requirements.txt +1 -0
  29. gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/README.md +0 -0
  30. gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/__init__.py +0 -0
  31. gatewaysdk/algorithm/gepa/lib/adapters/default_adapter/default_adapter.py +209 -0
  32. gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/README.md +7 -0
  33. gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/__init__.py +0 -0
  34. gatewaysdk/algorithm/gepa/lib/adapters/dspy_adapter/dspy_adapter.py +307 -0
  35. gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/README.md +99 -0
  36. gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
  37. gatewaysdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/full_program_adapter.py +268 -0
  38. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/GEPA_RAG.md +621 -0
  39. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/__init__.py +56 -0
  40. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
  41. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
  42. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
  43. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
  44. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
  45. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
  46. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
  47. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
  48. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
  49. gatewaysdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
  50. gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/README.md +552 -0
  51. gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/__init__.py +37 -0
  52. gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_adapter.py +699 -0
  53. gatewaysdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_client.py +364 -0
  54. gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/README.md +9 -0
  55. gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/__init__.py +0 -0
  56. gatewaysdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
  57. gatewaysdk/algorithm/gepa/lib/api.py +382 -0
  58. gatewaysdk/algorithm/gepa/lib/core/__init__.py +0 -0
  59. gatewaysdk/algorithm/gepa/lib/core/adapter.py +180 -0
  60. gatewaysdk/algorithm/gepa/lib/core/data_loader.py +74 -0
  61. gatewaysdk/algorithm/gepa/lib/core/engine.py +379 -0
  62. gatewaysdk/algorithm/gepa/lib/core/result.py +233 -0
  63. gatewaysdk/algorithm/gepa/lib/core/state.py +636 -0
  64. gatewaysdk/algorithm/gepa/lib/examples/__init__.py +0 -0
  65. gatewaysdk/algorithm/gepa/lib/examples/aime.py +24 -0
  66. gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/eval_default.py +111 -0
  67. gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/instruction_prompt.txt +9 -0
  68. gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/optimal_prompt.txt +24 -0
  69. gatewaysdk/algorithm/gepa/lib/examples/anymaths-bench/train_anymaths.py +177 -0
  70. gatewaysdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/arc_agi.ipynb +25705 -0
  71. gatewaysdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/example.ipynb +348 -0
  72. gatewaysdk/algorithm/gepa/lib/examples/mcp_adapter/__init__.py +4 -0
  73. gatewaysdk/algorithm/gepa/lib/examples/mcp_adapter/mcp_optimization_example.py +456 -0
  74. gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/RAG_GUIDE.md +613 -0
  75. gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/__init__.py +9 -0
  76. gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/rag_optimization.py +820 -0
  77. gatewaysdk/algorithm/gepa/lib/examples/rag_adapter/requirements-rag.txt +29 -0
  78. gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/instruction_prompt.txt +16 -0
  79. gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/terminus.txt +9 -0
  80. gatewaysdk/algorithm/gepa/lib/examples/terminal-bench/train_terminus.py +161 -0
  81. gatewaysdk/algorithm/gepa/lib/gepa_utils.py +117 -0
  82. gatewaysdk/algorithm/gepa/lib/logging/__init__.py +0 -0
  83. gatewaysdk/algorithm/gepa/lib/logging/experiment_tracker.py +187 -0
  84. gatewaysdk/algorithm/gepa/lib/logging/logger.py +75 -0
  85. gatewaysdk/algorithm/gepa/lib/logging/utils.py +103 -0
  86. gatewaysdk/algorithm/gepa/lib/proposer/__init__.py +0 -0
  87. gatewaysdk/algorithm/gepa/lib/proposer/base.py +31 -0
  88. gatewaysdk/algorithm/gepa/lib/proposer/merge.py +357 -0
  89. gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/__init__.py +0 -0
  90. gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/base.py +49 -0
  91. gatewaysdk/algorithm/gepa/lib/proposer/reflective_mutation/reflective_mutation.py +176 -0
  92. gatewaysdk/algorithm/gepa/lib/py.typed +0 -0
  93. gatewaysdk/algorithm/gepa/lib/strategies/__init__.py +0 -0
  94. gatewaysdk/algorithm/gepa/lib/strategies/batch_sampler.py +77 -0
  95. gatewaysdk/algorithm/gepa/lib/strategies/candidate_selector.py +50 -0
  96. gatewaysdk/algorithm/gepa/lib/strategies/component_selector.py +36 -0
  97. gatewaysdk/algorithm/gepa/lib/strategies/eval_policy.py +64 -0
  98. gatewaysdk/algorithm/gepa/lib/strategies/instruction_proposal.py +126 -0
  99. gatewaysdk/algorithm/gepa/lib/utils/__init__.py +10 -0
  100. gatewaysdk/algorithm/gepa/lib/utils/stop_condition.py +196 -0
  101. gatewaysdk/algorithm/gepa/tracing.py +105 -0
  102. gatewaysdk/algorithm/utils.py +177 -0
  103. gatewaysdk/algorithm/verl/__init__.py +5 -0
  104. gatewaysdk/algorithm/verl/interface.py +202 -0
  105. gatewaysdk/automations.py +111 -0
  106. gatewaysdk/benchmark_hub/README.md +88 -0
  107. gatewaysdk/benchmark_hub/__init__.py +119 -0
  108. gatewaysdk/benchmark_hub/_archive.py +189 -0
  109. gatewaysdk/benchmark_hub/_tar.py +6 -0
  110. gatewaysdk/benchmark_hub/client.py +992 -0
  111. gatewaysdk/benchmark_hub/dispatch_shard.py +34 -0
  112. gatewaysdk/benchmark_hub/eval_config.py +20 -0
  113. gatewaysdk/benchmark_hub/evals.py +351 -0
  114. gatewaysdk/benchmark_hub/harbor_adapter.py +673 -0
  115. gatewaysdk/benchmark_hub/path_utils.py +81 -0
  116. gatewaysdk/benchmark_hub/save_utils.py +101 -0
  117. gatewaysdk/benchmark_hub/verifiers_adapter.py +327 -0
  118. gatewaysdk/benchmark_hub/versioning.py +72 -0
  119. gatewaysdk/build.py +515 -0
  120. gatewaysdk/cli/__init__.py +58 -0
  121. gatewaysdk/cli/agent_runner.py +132 -0
  122. gatewaysdk/cli/http_client.py +115 -0
  123. gatewaysdk/cli/platform.py +4086 -0
  124. gatewaysdk/cli/prometheus.py +115 -0
  125. gatewaysdk/cli/release_gate.py +215 -0
  126. gatewaysdk/cli/store.py +131 -0
  127. gatewaysdk/cli/vllm.py +29 -0
  128. gatewaysdk/client.py +406 -0
  129. gatewaysdk/config.py +348 -0
  130. gatewaysdk/connectors/__init__.py +25 -0
  131. gatewaysdk/connectors/client.py +203 -0
  132. gatewaysdk/connectors/skill.py +25 -0
  133. gatewaysdk/connectors/template.py +106 -0
  134. gatewaysdk/context.py +606 -0
  135. gatewaysdk/emitter/__init__.py +43 -0
  136. gatewaysdk/emitter/annotation.py +370 -0
  137. gatewaysdk/emitter/exception.py +54 -0
  138. gatewaysdk/emitter/message.py +61 -0
  139. gatewaysdk/emitter/object.py +117 -0
  140. gatewaysdk/emitter/reward.py +320 -0
  141. gatewaysdk/env_var.py +156 -0
  142. gatewaysdk/environment/__init__.py +108 -0
  143. gatewaysdk/environment/_bundle.py +281 -0
  144. gatewaysdk/environment/_harbor.py +276 -0
  145. gatewaysdk/environment/_materialize.py +97 -0
  146. gatewaysdk/environment/_tar.py +33 -0
  147. gatewaysdk/environment/_world.py +346 -0
  148. gatewaysdk/environment/core.py +527 -0
  149. gatewaysdk/environment/errors.py +58 -0
  150. gatewaysdk/environment/runtime.py +150 -0
  151. gatewaysdk/environment/schema/__init__.py +35 -0
  152. gatewaysdk/environment/schema/__main__.py +225 -0
  153. gatewaysdk/environment/schema/_slack_fidelity.py +116 -0
  154. gatewaysdk/environment/schema/_slack_scenario.py +183 -0
  155. gatewaysdk/environment/schema/api.py +1628 -0
  156. gatewaysdk/environment/schema/batch.py +755 -0
  157. gatewaysdk/environment/schema/compiler.py +615 -0
  158. gatewaysdk/environment/schema/conform.py +428 -0
  159. gatewaysdk/environment/schema/connector.py +777 -0
  160. gatewaysdk/environment/schema/connectors/apple-business-manager/handlers.py +17 -0
  161. gatewaysdk/environment/schema/connectors/apple-business-manager/parity.json +28 -0
  162. gatewaysdk/environment/schema/connectors/apple-business-manager/provenance.json +233 -0
  163. gatewaysdk/environment/schema/connectors/apple-business-manager/scope.toml +112 -0
  164. gatewaysdk/environment/schema/connectors/apple-business-manager/world.json +1380 -0
  165. gatewaysdk/environment/schema/connectors/base.json +28 -0
  166. gatewaysdk/environment/schema/connectors/custom/handlers.py +18 -0
  167. gatewaysdk/environment/schema/connectors/custom/world.json +123 -0
  168. gatewaysdk/environment/schema/connectors/github/handlers.py +81 -0
  169. gatewaysdk/environment/schema/connectors/github/parity.json +37 -0
  170. gatewaysdk/environment/schema/connectors/github/provenance.json +134 -0
  171. gatewaysdk/environment/schema/connectors/github/scope.toml +158 -0
  172. gatewaysdk/environment/schema/connectors/github/world.json +5869 -0
  173. gatewaysdk/environment/schema/connectors/google-calendar/handlers.py +23 -0
  174. gatewaysdk/environment/schema/connectors/google-calendar/world.json +209 -0
  175. gatewaysdk/environment/schema/connectors/google-drive/handlers.py +18 -0
  176. gatewaysdk/environment/schema/connectors/google-drive/world.json +286 -0
  177. gatewaysdk/environment/schema/connectors/jamf/handlers.py +34 -0
  178. gatewaysdk/environment/schema/connectors/jamf/parity.json +49 -0
  179. gatewaysdk/environment/schema/connectors/jamf/provenance.json +181 -0
  180. gatewaysdk/environment/schema/connectors/jamf/scope.toml +128 -0
  181. gatewaysdk/environment/schema/connectors/jamf/world.json +2218 -0
  182. gatewaysdk/environment/schema/connectors/jira/handlers.py +28 -0
  183. gatewaysdk/environment/schema/connectors/jira/world.json +389 -0
  184. gatewaysdk/environment/schema/connectors/kandji/handlers.py +38 -0
  185. gatewaysdk/environment/schema/connectors/kandji/parity.json +29 -0
  186. gatewaysdk/environment/schema/connectors/kandji/provenance.json +152 -0
  187. gatewaysdk/environment/schema/connectors/kandji/scope.toml +120 -0
  188. gatewaysdk/environment/schema/connectors/kandji/world.json +765 -0
  189. gatewaysdk/environment/schema/connectors/linear/handlers.py +20 -0
  190. gatewaysdk/environment/schema/connectors/linear/world.json +363 -0
  191. gatewaysdk/environment/schema/connectors/microsoft-teams/fidelity.json +45 -0
  192. gatewaysdk/environment/schema/connectors/microsoft-teams/handlers.py +1 -0
  193. gatewaysdk/environment/schema/connectors/microsoft-teams/provenance.json +851 -0
  194. gatewaysdk/environment/schema/connectors/microsoft-teams/scope.md +231 -0
  195. gatewaysdk/environment/schema/connectors/microsoft-teams/teams-types.json +1710 -0
  196. gatewaysdk/environment/schema/connectors/microsoft-teams/world.json +858 -0
  197. gatewaysdk/environment/schema/connectors/netsuite/handlers.py +32 -0
  198. gatewaysdk/environment/schema/connectors/netsuite/parity.json +50 -0
  199. gatewaysdk/environment/schema/connectors/netsuite/provenance.json +137 -0
  200. gatewaysdk/environment/schema/connectors/netsuite/scope.toml +78 -0
  201. gatewaysdk/environment/schema/connectors/netsuite/world.json +16243 -0
  202. gatewaysdk/environment/schema/connectors/salesforce/handlers.py +806 -0
  203. gatewaysdk/environment/schema/connectors/salesforce/parity.json +46 -0
  204. gatewaysdk/environment/schema/connectors/salesforce/provenance.json +143 -0
  205. gatewaysdk/environment/schema/connectors/salesforce/scope.toml +174 -0
  206. gatewaysdk/environment/schema/connectors/salesforce/world.json +4695 -0
  207. gatewaysdk/environment/schema/connectors/slack/MODEL.md +236 -0
  208. gatewaysdk/environment/schema/connectors/slack/capabilities.json +1039 -0
  209. gatewaysdk/environment/schema/connectors/slack/handlers.py +762 -0
  210. gatewaysdk/environment/schema/connectors/slack/parity.json +65 -0
  211. gatewaysdk/environment/schema/connectors/slack/provenance.json +294 -0
  212. gatewaysdk/environment/schema/connectors/slack/scope.toml +44 -0
  213. gatewaysdk/environment/schema/connectors/slack/world.json +4993 -0
  214. gatewaysdk/environment/schema/connectors/workday/handlers.py +75 -0
  215. gatewaysdk/environment/schema/connectors/workday/parity.json +36 -0
  216. gatewaysdk/environment/schema/connectors/workday/provenance.json +179 -0
  217. gatewaysdk/environment/schema/connectors/workday/scope.toml +180 -0
  218. gatewaysdk/environment/schema/connectors/workday/world.json +806 -0
  219. gatewaysdk/environment/schema/data.py +593 -0
  220. gatewaysdk/environment/schema/extract.py +263 -0
  221. gatewaysdk/environment/schema/host.py +2437 -0
  222. gatewaysdk/environment/schema/host_surfaces.py +275 -0
  223. gatewaysdk/environment/schema/platform.py +1272 -0
  224. gatewaysdk/environment/schema/scaffold.py +250 -0
  225. gatewaysdk/environment/schema/skill.py +118 -0
  226. gatewaysdk/environment/schema/skills/agents/connector-template-author.md +202 -0
  227. gatewaysdk/environment/schema/skills/connector-schema-authoring/CONNECTORS.md +331 -0
  228. gatewaysdk/environment/schema/skills/connector-schema-authoring/CONVENTIONS.md +100 -0
  229. gatewaysdk/environment/schema/skills/connector-schema-authoring/SKILL.md +313 -0
  230. gatewaysdk/environment/schema/skills/world-data-ingestion/ROWS.md +223 -0
  231. gatewaysdk/environment/schema/skills/world-data-ingestion/SKILL.md +293 -0
  232. gatewaysdk/environment/schema/skills/worlds-getting-started/SKILL.md +390 -0
  233. gatewaysdk/environment/schema/slack.py +757 -0
  234. gatewaysdk/environment/schema/snapshot.py +479 -0
  235. gatewaysdk/environment/schema/store.py +904 -0
  236. gatewaysdk/environment/schema/tasks.py +525 -0
  237. gatewaysdk/environment/schema/tools_world.py +322 -0
  238. gatewaysdk/environment/schema/validation.py +577 -0
  239. gatewaysdk/environment/schema/worker.py +78 -0
  240. gatewaysdk/environment/schema/workers.py +500 -0
  241. gatewaysdk/environment/schema/world_tests.py +336 -0
  242. gatewaysdk/environments/__init__.py +24 -0
  243. gatewaysdk/environments/client.py +486 -0
  244. gatewaysdk/environments/types.py +318 -0
  245. gatewaysdk/execution/__init__.py +15 -0
  246. gatewaysdk/execution/base.py +64 -0
  247. gatewaysdk/execution/client_server.py +443 -0
  248. gatewaysdk/execution/events.py +69 -0
  249. gatewaysdk/execution/inter_process.py +16 -0
  250. gatewaysdk/execution/shared_memory.py +282 -0
  251. gatewaysdk/experiments/__init__.py +90 -0
  252. gatewaysdk/experiments/assignment.py +177 -0
  253. gatewaysdk/experiments/client.py +877 -0
  254. gatewaysdk/experiments/exposure.py +222 -0
  255. gatewaysdk/experiments/types.py +81 -0
  256. gatewaysdk/importers/__init__.py +49 -0
  257. gatewaysdk/importers/_normalize.py +91 -0
  258. gatewaysdk/importers/builder.py +173 -0
  259. gatewaysdk/importers/client.py +95 -0
  260. gatewaysdk/importers/langsmith.py +196 -0
  261. gatewaysdk/importers/recipes.py +249 -0
  262. gatewaysdk/instrumentation/__init__.py +300 -0
  263. gatewaysdk/instrumentation/agentops.py +314 -0
  264. gatewaysdk/instrumentation/agentops_langchain.py +45 -0
  265. gatewaysdk/instrumentation/base.py +119 -0
  266. gatewaysdk/instrumentation/litellm.py +83 -0
  267. gatewaysdk/instrumentation/registry.py +273 -0
  268. gatewaysdk/instrumentation/vllm.py +81 -0
  269. gatewaysdk/instrumentation/weave.py +500 -0
  270. gatewaysdk/integrations/__init__.py +15 -0
  271. gatewaysdk/integrations/gateway/__init__.py +11 -0
  272. gatewaysdk/integrations/gateway/client.py +171 -0
  273. gatewaysdk/integrations/tool_access.py +549 -0
  274. gatewaysdk/litagent/__init__.py +11 -0
  275. gatewaysdk/litagent/decorator.py +536 -0
  276. gatewaysdk/litagent/litagent.py +252 -0
  277. gatewaysdk/llm_proxy.py +1742 -0
  278. gatewaysdk/logging.py +370 -0
  279. gatewaysdk/memory.py +278 -0
  280. gatewaysdk/personas.py +186 -0
  281. gatewaysdk/platform/__init__.py +17 -0
  282. gatewaysdk/platform/builder.py +221 -0
  283. gatewaysdk/platform/compatibility.py +67 -0
  284. gatewaysdk/platform/manifest.py +185 -0
  285. gatewaysdk/platform/orchestrator.py +901 -0
  286. gatewaysdk/platform/registry.py +122 -0
  287. gatewaysdk/platform/worker.py +864 -0
  288. gatewaysdk/replay/__init__.py +1032 -0
  289. gatewaysdk/replay/pytest.py +56 -0
  290. gatewaysdk/reward.py +7 -0
  291. gatewaysdk/run.py +1781 -0
  292. gatewaysdk/runner/__init__.py +11 -0
  293. gatewaysdk/runner/agent.py +878 -0
  294. gatewaysdk/runner/base.py +182 -0
  295. gatewaysdk/runner/legacy.py +309 -0
  296. gatewaysdk/security.py +700 -0
  297. gatewaysdk/semconv.py +170 -0
  298. gatewaysdk/server.py +399 -0
  299. gatewaysdk/sessions.py +282 -0
  300. gatewaysdk/store/__init__.py +45 -0
  301. gatewaysdk/store/base.py +908 -0
  302. gatewaysdk/store/client_server.py +2093 -0
  303. gatewaysdk/store/collection/__init__.py +30 -0
  304. gatewaysdk/store/collection/base.py +587 -0
  305. gatewaysdk/store/collection/memory.py +970 -0
  306. gatewaysdk/store/collection/mongo.py +1412 -0
  307. gatewaysdk/store/collection_based.py +1823 -0
  308. gatewaysdk/store/gateway.py +983 -0
  309. gatewaysdk/store/gateway_listener.py +465 -0
  310. gatewaysdk/store/listener.py +58 -0
  311. gatewaysdk/store/memory.py +396 -0
  312. gatewaysdk/store/mongo.py +165 -0
  313. gatewaysdk/store/redis_stream.py +517 -0
  314. gatewaysdk/store/sqlite.py +3 -0
  315. gatewaysdk/store/threading.py +370 -0
  316. gatewaysdk/store/utils.py +142 -0
  317. gatewaysdk/tracer/__init__.py +14 -0
  318. gatewaysdk/tracer/base.py +286 -0
  319. gatewaysdk/tracer/dummy.py +106 -0
  320. gatewaysdk/tracer/otel.py +559 -0
  321. gatewaysdk/tracing/__init__.py +110 -0
  322. gatewaysdk/tracing/api.py +808 -0
  323. gatewaysdk/tracing/attributes.py +9 -0
  324. gatewaysdk/tracing/context.py +272 -0
  325. gatewaysdk/tracing/exporters/__init__.py +10 -0
  326. gatewaysdk/tracing/exporters/gateway.py +228 -0
  327. gatewaysdk/tracing/identity.py +288 -0
  328. gatewaysdk/tracing/init.py +620 -0
  329. gatewaysdk/tracing/instrumentors/__init__.py +15 -0
  330. gatewaysdk/tracing/instrumentors/claude_agent_sdk.py +766 -0
  331. gatewaysdk/tracing/instrumentors/instrumentation_principles.md +294 -0
  332. gatewaysdk/tracing/instrumentors/registry.py +352 -0
  333. gatewaysdk/tracing/mapping.py +729 -0
  334. gatewaysdk/tracing/processors.py +393 -0
  335. gatewaysdk/tracing/push.py +617 -0
  336. gatewaysdk/tracing/push_models.py +247 -0
  337. gatewaysdk/tracing/semconv.py +294 -0
  338. gatewaysdk/tracing/span_builder.py +356 -0
  339. gatewaysdk/trainer/__init__.py +6 -0
  340. gatewaysdk/trainer/init_utils.py +263 -0
  341. gatewaysdk/trainer/legacy.py +359 -0
  342. gatewaysdk/trainer/registry.py +12 -0
  343. gatewaysdk/trainer/trainer.py +638 -0
  344. gatewaysdk/types/__init__.py +63 -0
  345. gatewaysdk/types/core.py +556 -0
  346. gatewaysdk/types/resources.py +204 -0
  347. gatewaysdk/types/tracer.py +515 -0
  348. gatewaysdk/types/tracing.py +162 -0
  349. gatewaysdk/users.py +251 -0
  350. gatewaysdk/utils/__init__.py +1 -0
  351. gatewaysdk/utils/id.py +18 -0
  352. gatewaysdk/utils/metrics.py +1025 -0
  353. gatewaysdk/utils/otel.py +550 -0
  354. gatewaysdk/utils/otlp.py +556 -0
  355. gatewaysdk/utils/redact.py +22 -0
  356. gatewaysdk/utils/server_launcher.py +1045 -0
  357. gatewaysdk/utils/system_snapshot.py +90 -0
  358. gatewaysdk/verl/__init__.py +8 -0
  359. gatewaysdk/verl/__main__.py +6 -0
  360. gatewaysdk/verl/async_server.py +46 -0
  361. gatewaysdk/verl/config.yaml +27 -0
  362. gatewaysdk/verl/daemon.py +1154 -0
  363. gatewaysdk/verl/dataset.py +44 -0
  364. gatewaysdk/verl/entrypoint.py +248 -0
  365. gatewaysdk/verl/trainer.py +549 -0
  366. gatewaysdk/world_browser.py +748 -0
  367. gatewaysdk/world_data.py +453 -0
  368. gatewaysdk/world_sessions.py +978 -0
  369. gatewaysdk/world_tasks.py +285 -0
  370. gatewaysdk/world_tools.py +115 -0
  371. gatewaysdk/worlds.py +639 -0
  372. gatewaysdk-0.3.2.dist-info/METADATA +236 -0
  373. gatewaysdk-0.3.2.dist-info/RECORD +376 -0
  374. gatewaysdk-0.3.2.dist-info/WHEEL +4 -0
  375. gatewaysdk-0.3.2.dist-info/entry_points.txt +5 -0
  376. gatewaysdk-0.3.2.dist-info/licenses/LICENSE +19 -0
@@ -0,0 +1,527 @@
1
+ """``gwenv.init`` and the :class:`Environment` object (spec §4).
2
+
3
+ import gatewaysdk.environment as gwenv
4
+
5
+ env = gwenv.init("refund-flows-env") # slug or id; READY head
6
+ with env: # implicit world + ambient interception
7
+ my_agent("handle ticket #4415")
8
+ result = env.verify("refund_row_created")
9
+ assert result.passed, env.state("SELECT * FROM refunds")
10
+
11
+ Local mode only in P1: the version bundle is pulled via the existing
12
+ environments/benchmark-hub clients, extracted to a cache dir, and executed
13
+ in-process (sqlite worlds for ``mock-tools``; per-task docker sandboxes for
14
+ ``harbor``). ``mode="remote"`` and ``hosts=`` transport patching are P2/P3.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import inspect
20
+ import os
21
+ import tempfile
22
+ import time
23
+ from contextlib import contextmanager
24
+ from contextvars import ContextVar
25
+ from pathlib import Path
26
+ from typing import Any, Dict, Iterable, Iterator, List, Mapping, Optional, Union
27
+
28
+ from .. import build as _build
29
+ from ..benchmark_hub.client import BenchmarkPushError, GatewayBenchmarkClient
30
+ from ..environments import GatewayEnvironmentClient, GatewayEnvironmentError
31
+ from ._bundle import (
32
+ KIND_HARBOR,
33
+ KIND_MOCK_TOOLS,
34
+ EnvManifest,
35
+ HarborTask,
36
+ detect_kind,
37
+ load_handlers,
38
+ load_harbor_tasks,
39
+ parse_manifest,
40
+ )
41
+ from ._harbor import CliDockerRuntime, DockerRuntime, HarborWorld
42
+ from ._world import _NO_INPUT, VerifierResult, World, WorldRegistry
43
+ from .errors import (
44
+ EnvironmentKindError,
45
+ EnvironmentNotConfigured,
46
+ EnvironmentNotReady,
47
+ HermeticViolation,
48
+ ToolRouteError,
49
+ WorldStartError,
50
+ )
51
+
52
+ #: Env traffic records under ``gateway-env-{slug}`` — the tracing environment
53
+ #: tag that keeps it out of production views (sibling of replay's
54
+ #: ``gateway-replay``).
55
+ ENV_ENVIRONMENT_PREFIX = "gateway-env-"
56
+
57
+ _ROUTES = ("env", "prod")
58
+
59
+ #: The ambiently-active world (set by ``with env:`` / ``env.branch()``); the
60
+ #: ``define_tool`` wrapper consults it on every call.
61
+ _active_world: ContextVar[Optional[World]] = ContextVar("gateway_env_world", default=None)
62
+
63
+
64
+ def get_active_world() -> Optional[World]:
65
+ return _active_world.get()
66
+
67
+
68
+ class RouteRecord:
69
+ """One routed call: where it went and how long it took."""
70
+
71
+ __slots__ = ("tool", "route", "duration_ms", "world_id")
72
+
73
+ def __init__(self, tool: str, route: str, duration_ms: float, world_id: Optional[str]) -> None:
74
+ self.tool = tool
75
+ self.route = route
76
+ self.duration_ms = duration_ms
77
+ self.world_id = world_id
78
+
79
+ def __repr__(self) -> str: # pragma: no cover — debugging nicety
80
+ return f"RouteRecord({self.tool!r}, {self.route!r}, {self.duration_ms:.1f}ms, world={self.world_id})"
81
+
82
+
83
+ class Report:
84
+ """Route provenance + hermetic accounting for one Environment."""
85
+
86
+ def __init__(self) -> None:
87
+ self.routes: List[RouteRecord] = []
88
+
89
+ @property
90
+ def hermetic(self) -> bool:
91
+ """False if ANY call reached prod."""
92
+ return not self.prod_calls()
93
+
94
+ def prod_calls(self) -> List[RouteRecord]:
95
+ return [record for record in self.routes if record.route == "prod"]
96
+
97
+
98
+ class Environment:
99
+ """A resolved environment bundle plus its routing/runtime configuration.
100
+
101
+ Construct via :func:`init` (platform) or :func:`init_from_path` (local
102
+ bundle dir) rather than directly.
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ bundle_path: Union[str, Path],
108
+ *,
109
+ slug: Optional[str] = None,
110
+ version: Optional[str] = None,
111
+ route: Optional[Mapping[str, str]] = None,
112
+ default_route: str = "env",
113
+ unsafe_allow_prod: Iterable[str] = (),
114
+ record: bool = True,
115
+ docker: Optional[DockerRuntime] = None,
116
+ ) -> None:
117
+ self.bundle_path = Path(bundle_path)
118
+ if not self.bundle_path.is_dir():
119
+ raise EnvironmentNotReady(f"bundle path does not exist: {self.bundle_path}")
120
+ self.kind = detect_kind(self.bundle_path)
121
+ self.version = version
122
+ self.record = record
123
+
124
+ if default_route not in _ROUTES:
125
+ raise ValueError(f"default_route must be one of {_ROUTES}, got {default_route!r}")
126
+ self._route: Dict[str, str] = dict(route or {})
127
+ for tool_name, target in self._route.items():
128
+ if target not in _ROUTES:
129
+ raise ValueError(f"route[{tool_name!r}] must be one of {_ROUTES}, got {target!r}")
130
+ self._default_route = default_route
131
+ self._unsafe_allow_prod = set(unsafe_allow_prod)
132
+ self.report = Report()
133
+ self._docker_runtime = docker
134
+
135
+ self.manifest: Optional[EnvManifest] = None
136
+ self._handlers: Dict[str, Any] = {}
137
+ self._harbor_tasks: List[HarborTask] = []
138
+ self._implicit_world: Optional[World] = None
139
+ self._enter_token: Any = None
140
+ self._tracing_cm: Any = None
141
+
142
+ if self.kind == KIND_MOCK_TOOLS:
143
+ self.manifest = parse_manifest(self.bundle_path)
144
+ self.slug = slug or self.manifest.name
145
+ self._handlers = load_handlers(self.bundle_path, self.manifest)
146
+ self._tmpdir = tempfile.TemporaryDirectory(prefix=f"gwenv-{_safe(self.slug)}-")
147
+ self._worlds_dir = Path(self._tmpdir.name)
148
+ self._template_db = self._worlds_dir / "template.db"
149
+ self._build_template_db()
150
+ else:
151
+ self.slug = slug or self.bundle_path.name
152
+ self._harbor_tasks = load_harbor_tasks(self.bundle_path)
153
+
154
+ _install_ambient_interceptor()
155
+
156
+ # -- kind guards -----------------------------------------------------
157
+
158
+ def _require_mock(self, operation: str) -> None:
159
+ if self.kind != KIND_MOCK_TOOLS:
160
+ raise EnvironmentKindError(
161
+ f"{operation} is a mock-tools seam, but '{self.slug}' is a "
162
+ f"{self.kind} environment — harbor envs are task sandboxes, not "
163
+ "tool mocks. Use env.tasks / env.task(name), world.exec(...), "
164
+ "and world.verify() instead."
165
+ )
166
+
167
+ def _require_harbor(self, operation: str) -> None:
168
+ if self.kind != KIND_HARBOR:
169
+ raise EnvironmentKindError(
170
+ f"{operation} is a harbor seam, but '{self.slug}' is a "
171
+ f"{self.kind} environment — use `with env:` / env.branch() and "
172
+ "the registry/execute seams instead."
173
+ )
174
+
175
+ # -- mock-tools: template + worlds ------------------------------------
176
+
177
+ def _build_template_db(self) -> None:
178
+ from ._materialize import materialize_template
179
+
180
+ materialize_template(self.bundle_path, self._template_db)
181
+
182
+ @contextmanager
183
+ def branch(self) -> Iterator[World]:
184
+ """A fresh world (file copy of the seed) — run N is identical to run 1.
185
+ The world is also ambiently active inside the block."""
186
+ self._require_mock("env.branch()")
187
+ world = World(self)
188
+ token = _active_world.set(world)
189
+ try:
190
+ yield world
191
+ finally:
192
+ _active_world.reset(token)
193
+
194
+ def __enter__(self) -> "Environment":
195
+ self._require_mock("with env:")
196
+ if self._implicit_world is not None:
197
+ self._implicit_world.close()
198
+ self._implicit_world = World(self)
199
+ self._enter_token = _active_world.set(self._implicit_world)
200
+ self._tracing_cm = self._enter_tracing_context()
201
+ return self
202
+
203
+ def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
204
+ if self._enter_token is not None:
205
+ _active_world.reset(self._enter_token)
206
+ self._enter_token = None
207
+ if self._tracing_cm is not None:
208
+ try:
209
+ self._tracing_cm.__exit__(exc_type, exc, tb)
210
+ except Exception: # noqa: BLE001 — tracing is best-effort
211
+ pass
212
+ self._tracing_cm = None
213
+ # The implicit world stays alive: env.verify()/env.state() inspect the
214
+ # end state AFTER the block (spec §4.1 quickstart).
215
+
216
+ def _enter_tracing_context(self) -> Any:
217
+ """Tag traces with environment=gateway-env-{slug} while inside
218
+ ``with env:`` — best-effort, mirroring replay's REPLAY_ENVIRONMENT."""
219
+ try:
220
+ from ..tracing.exporters.gateway import is_gateway_configured
221
+
222
+ if not is_gateway_configured():
223
+ return None
224
+ from ..tracing.context import tracing_context
225
+
226
+ cm = tracing_context(environment=f"{ENV_ENVIRONMENT_PREFIX}{self.slug}")
227
+ cm.__enter__()
228
+ return cm
229
+ except Exception: # noqa: BLE001 — tracing is best-effort
230
+ return None
231
+
232
+ def _ensure_world(self) -> World:
233
+ self._require_mock("the implicit world")
234
+ if self._implicit_world is None:
235
+ self._implicit_world = World(self)
236
+ return self._implicit_world
237
+
238
+ # -- delegates to the implicit world ----------------------------------
239
+
240
+ def execute(self, tool_name: str, tool_input: Any = _NO_INPUT) -> Any:
241
+ return self._ensure_world().execute(tool_name, tool_input)
242
+
243
+ def as_registry(self) -> WorldRegistry:
244
+ return self._ensure_world().as_registry()
245
+
246
+ def wrap_tools(self, tools: Any) -> Any:
247
+ return self._ensure_world().wrap_tools(tools)
248
+
249
+ def verify(self, name: str) -> VerifierResult:
250
+ return self._ensure_world().verify(name)
251
+
252
+ def state(self, sql: str, *params: Any) -> List[Dict[str, Any]]:
253
+ return self._ensure_world().state(sql, *params)
254
+
255
+ def reset(self) -> None:
256
+ self._ensure_world().reset()
257
+
258
+ # -- harbor ----------------------------------------------------------
259
+
260
+ @property
261
+ def tasks(self) -> List[HarborTask]:
262
+ """The env's Harbor tasks (harbor kind only)."""
263
+ self._require_harbor("env.tasks")
264
+ return list(self._harbor_tasks)
265
+
266
+ @contextmanager
267
+ def task(self, name: str) -> Iterator[HarborWorld]:
268
+ """Boot one harbor task's docker sandbox; cleaned up on exit."""
269
+ self._require_harbor("env.task()")
270
+ matches = [task for task in self._harbor_tasks if task.name == name]
271
+ if not matches:
272
+ raise KeyError(
273
+ f"no harbor task '{name}' in '{self.slug}' "
274
+ f"(tasks: {[task.name for task in self._harbor_tasks]})"
275
+ )
276
+ world = HarborWorld(self, matches[0], self._docker())
277
+ try:
278
+ yield world
279
+ finally:
280
+ world.close()
281
+
282
+ def _docker(self) -> DockerRuntime:
283
+ if self._docker_runtime is None:
284
+ self._docker_runtime = CliDockerRuntime()
285
+ return self._docker_runtime
286
+
287
+ # -- routing ----------------------------------------------------------
288
+
289
+ def resolve_route(self, tool_name: str) -> str:
290
+ """Route map > effects default (read→default_route, write→env,
291
+ irreversible→never prod unless unsafe_allow_prod) > default_route."""
292
+ explicit = self._route.get(tool_name)
293
+ effects = _effects_of(tool_name)
294
+ if explicit is not None:
295
+ route = explicit
296
+ elif effects in ("write", "irreversible"):
297
+ route = "env"
298
+ else: # "read" or unregistered
299
+ route = self._default_route
300
+ if route == "prod" and effects == "irreversible" and tool_name not in self._unsafe_allow_prod:
301
+ raise ToolRouteError(
302
+ f"tool '{tool_name}' is declared effects='irreversible' — refusing "
303
+ "the prod route. Pass unsafe_allow_prod=['" + tool_name + "'] to "
304
+ "override (you probably don't want to)."
305
+ )
306
+ return route
307
+
308
+ def _record_route(self, tool: str, route: str, duration_ms: float, world_id: Optional[str]) -> None:
309
+ self.report.routes.append(RouteRecord(tool, route, duration_ms, world_id))
310
+
311
+ def assert_hermetic(self) -> None:
312
+ """Raise :class:`HermeticViolation` if any call reached prod."""
313
+ offending = self.report.prod_calls()
314
+ if offending:
315
+ lines = "\n".join(
316
+ f" - {record.tool} ({record.duration_ms:.1f}ms)" for record in offending
317
+ )
318
+ raise HermeticViolation(
319
+ f"{len(offending)} call(s) reached prod during '{self.slug}':\n{lines}"
320
+ )
321
+
322
+ def close(self) -> None:
323
+ """Release worlds and temp state (also happens at GC/interpreter exit)."""
324
+ if self._implicit_world is not None:
325
+ self._implicit_world.close()
326
+ self._implicit_world = None
327
+ if self.kind == KIND_MOCK_TOOLS:
328
+ self._tmpdir.cleanup()
329
+
330
+
331
+ def _safe(name: str) -> str:
332
+ return "".join(ch if ch.isalnum() or ch in "-_" else "-" for ch in name)[:40]
333
+
334
+
335
+ def _effects_of(tool_name: str) -> Optional[str]:
336
+ for identity in _build.get_registered_tools():
337
+ if identity.get("name") == tool_name:
338
+ return identity.get("effects")
339
+ return None
340
+
341
+
342
+ # ---------------------------------------------------------------------------
343
+ # Ambient interception — the define_tool wrapper's hook (spec §4.4, rung 1.5)
344
+ # ---------------------------------------------------------------------------
345
+
346
+
347
+ def _ambient_intercept(name: str, fn: Any, args: tuple, kwargs: dict) -> Any:
348
+ """Called by the ``define_tool`` wrapper on every invocation. No active
349
+ world → miss sentinel (the wrapper calls the original fn — the identity
350
+ path). Active world → route: env executes the bundle handler against the
351
+ world; prod passes through to the original fn WITH provenance logged."""
352
+ world = _active_world.get()
353
+ if world is None:
354
+ return _build._TOOL_INTERCEPT_MISS
355
+ env = world.env
356
+ route = env.resolve_route(name)
357
+
358
+ if route == "env":
359
+ if name not in env._handlers:
360
+ raise ToolRouteError(
361
+ f"tool '{name}' routes to env but '{env.slug}' serves no handler "
362
+ f"for it (served: {sorted(env._handlers)}) — add a handler, "
363
+ "route={'" + name + "': 'prod'}, or default_route='prod'."
364
+ )
365
+ tool_input = _bind_tool_input(fn, args, kwargs)
366
+ if inspect.iscoroutinefunction(fn):
367
+ async def _async_execute() -> Any:
368
+ return world.execute(name, tool_input)
369
+
370
+ return _async_execute()
371
+ return world.execute(name, tool_input)
372
+
373
+ # prod passthrough — the original fn IS the live implementation.
374
+ started = time.perf_counter()
375
+ if inspect.iscoroutinefunction(fn):
376
+ async def _async_prod() -> Any:
377
+ try:
378
+ return await fn(*args, **kwargs)
379
+ finally:
380
+ env._record_route(name, "prod", (time.perf_counter() - started) * 1000.0, world.id)
381
+
382
+ return _async_prod()
383
+ try:
384
+ return fn(*args, **kwargs)
385
+ finally:
386
+ env._record_route(name, "prod", (time.perf_counter() - started) * 1000.0, world.id)
387
+
388
+
389
+ def _bind_tool_input(fn: Any, args: tuple, kwargs: dict) -> Any:
390
+ """Map the wrapped fn's call arguments to a kwargs dict for the env
391
+ handler (handlers mirror the real tool's parameter names)."""
392
+ if not args:
393
+ return dict(kwargs)
394
+ try:
395
+ bound = inspect.signature(fn).bind(*args, **kwargs)
396
+ return dict(bound.arguments)
397
+ except (TypeError, ValueError):
398
+ if len(args) == 1 and not kwargs:
399
+ return args[0]
400
+ return {"args": list(args), **kwargs}
401
+
402
+
403
+ _interceptor_installed = False
404
+
405
+
406
+ def _install_ambient_interceptor() -> None:
407
+ global _interceptor_installed
408
+ if not _interceptor_installed:
409
+ _build.set_tool_call_interceptor(_ambient_intercept)
410
+ _interceptor_installed = True
411
+
412
+
413
+ # ---------------------------------------------------------------------------
414
+ # init
415
+ # ---------------------------------------------------------------------------
416
+
417
+
418
+ def init_from_path(
419
+ path: Union[str, Path],
420
+ *,
421
+ slug: Optional[str] = None,
422
+ route: Optional[Mapping[str, str]] = None,
423
+ default_route: str = "env",
424
+ unsafe_allow_prod: Iterable[str] = (),
425
+ record: bool = True,
426
+ docker: Optional[DockerRuntime] = None,
427
+ ) -> Environment:
428
+ """Boot an environment straight from a local bundle directory — no
429
+ platform round-trip. The offline/dev/test twin of :func:`init`."""
430
+ return Environment(
431
+ path,
432
+ slug=slug,
433
+ route=route,
434
+ default_route=default_route,
435
+ unsafe_allow_prod=unsafe_allow_prod,
436
+ record=record,
437
+ docker=docker,
438
+ )
439
+
440
+
441
+ def init(
442
+ ref: str,
443
+ *,
444
+ version: Optional[str] = None,
445
+ mode: str = "local",
446
+ route: Optional[Mapping[str, str]] = None,
447
+ default_route: str = "env",
448
+ hosts: Optional[Mapping[str, str]] = None,
449
+ unsafe_allow_prod: Iterable[str] = (),
450
+ record: bool = True,
451
+ host: Optional[str] = None,
452
+ public_key: Optional[str] = None,
453
+ secret_key: Optional[str] = None,
454
+ docker: Optional[DockerRuntime] = None,
455
+ ) -> Environment:
456
+ """Resolve ``ref`` (env slug or container id) on the platform, pull the
457
+ READY version bundle (or ``version`` — pin in CI!), and boot it locally.
458
+
459
+ Missing keys/config raise :class:`EnvironmentNotConfigured` here, never
460
+ mid-run. ``mode="remote"`` (hosted worlds) and ``hosts=`` (transport-level
461
+ host routing) are not part of P1.
462
+ """
463
+ if mode == "remote":
464
+ raise NotImplementedError(
465
+ "mode='remote' (platform-hosted worlds) lands with the mock-instance "
466
+ "manager — spec §4.9, phase P3. Use mode='local'."
467
+ )
468
+ if mode != "local":
469
+ raise ValueError(f"mode must be 'local' or 'remote', got {mode!r}")
470
+ if hosts:
471
+ raise NotImplementedError("hosts= transport routing lands in P2 — route tools, not hosts, for now.")
472
+
473
+ resolved_host = host or os.environ.get("GATEWAY_HOST") or ""
474
+ resolved_public = public_key or os.environ.get("GATEWAY_PUBLIC_KEY") or ""
475
+ resolved_secret = secret_key or os.environ.get("GATEWAY_SECRET_KEY") or ""
476
+ if not (resolved_host and resolved_public and resolved_secret):
477
+ raise EnvironmentNotConfigured(
478
+ "Gateway is not configured — set GATEWAY_HOST, GATEWAY_PUBLIC_KEY and "
479
+ "GATEWAY_SECRET_KEY (or pass host=/public_key=/secret_key=)."
480
+ )
481
+
482
+ env_client = GatewayEnvironmentClient(resolved_host, resolved_public, resolved_secret)
483
+ slug: Optional[str] = None
484
+ listing = env_client.list_environments(slug=ref)
485
+ if listing.environments:
486
+ slug = listing.environments[0].slug
487
+ else:
488
+ try:
489
+ slug = env_client.get_environment(ref).slug
490
+ except GatewayEnvironmentError:
491
+ raise EnvironmentNotReady(
492
+ f"environment '{ref}' not found (by slug or id) on {resolved_host}"
493
+ ) from None
494
+
495
+ bench = GatewayBenchmarkClient(resolved_host, resolved_public, resolved_secret)
496
+ try:
497
+ details = bench.resolve_version(slug, version or "latest")
498
+ except BenchmarkPushError as error:
499
+ raise EnvironmentNotReady(f"no runnable version for '{slug}': {error}") from None
500
+ content_hash = details.get("contentHash")
501
+ if not details.get("downloadUrl"):
502
+ raise EnvironmentNotReady(
503
+ f"'{slug}'@{version or 'latest'} resolved but has no uploaded bundle — "
504
+ "the version never finished building (no READY head)."
505
+ )
506
+
507
+ cache_root = Path(
508
+ os.environ.get("GATEWAY_ENV_CACHE") or Path.home() / ".cache" / "gatewaysdk" / "environments"
509
+ )
510
+ bundle_dir = cache_root / slug / (content_hash or version or "latest")
511
+ if not (bundle_dir.is_dir() and any(bundle_dir.iterdir())):
512
+ bundle_dir.mkdir(parents=True, exist_ok=True)
513
+ try:
514
+ bench.pull(f"{slug}@{version or content_hash or 'latest'}", target=str(bundle_dir))
515
+ except BenchmarkPushError as error:
516
+ raise EnvironmentNotReady(f"failed to pull '{slug}' bundle: {error}") from None
517
+
518
+ return Environment(
519
+ bundle_dir,
520
+ slug=slug,
521
+ version=content_hash or version,
522
+ route=route,
523
+ default_route=default_route,
524
+ unsafe_allow_prod=unsafe_allow_prod,
525
+ record=record,
526
+ docker=docker,
527
+ )
@@ -0,0 +1,58 @@
1
+ """Errors for ``gatewaysdk.environment`` (spec §4.7).
2
+
3
+ All errors subclass :class:`GatewayEnvError` so callers can catch the whole
4
+ family with one except clause. The §4.7 set is fixed; ``EnvironmentKindError``
5
+ is an addendum addition for kind-mismatched API use (mock-tools seams on a
6
+ harbor env and vice versa).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+
12
+ class GatewayEnvError(RuntimeError):
13
+ """Base error for the environment SDK."""
14
+
15
+
16
+ class EnvironmentNotConfigured(GatewayEnvError):
17
+ """Missing host/keys — raised at ``init()``, never mid-run."""
18
+
19
+
20
+ class EnvironmentNotReady(GatewayEnvError):
21
+ """No READY version to run against (or the env/bundle cannot be resolved)."""
22
+
23
+
24
+ class WorldStartError(GatewayEnvError):
25
+ """A world failed to boot (sqlite template build, docker run, …) — the
26
+ message carries a remediation hint."""
27
+
28
+
29
+ class ToolRouteError(GatewayEnvError):
30
+ """A tool call had nowhere to go: no env handler and no prod route, or a
31
+ route the current seam cannot honor (e.g. prod at the registry seam)."""
32
+
33
+
34
+ class HermeticViolation(GatewayEnvError):
35
+ """Raised by ``env.assert_hermetic()`` when any call reached prod —
36
+ the message lists the offending calls."""
37
+
38
+
39
+ class VerifierError(GatewayEnvError):
40
+ """Unknown verifier or verifier execution error, with the SQL/script
41
+ echoed for debugging."""
42
+
43
+
44
+ class EnvironmentKindError(GatewayEnvError):
45
+ """An API surface was used on the wrong environment kind (harbor task
46
+ sandboxes vs mock-tools worlds)."""
47
+
48
+
49
+ __all__ = [
50
+ "GatewayEnvError",
51
+ "EnvironmentNotConfigured",
52
+ "EnvironmentNotReady",
53
+ "WorldStartError",
54
+ "ToolRouteError",
55
+ "HermeticViolation",
56
+ "VerifierError",
57
+ "EnvironmentKindError",
58
+ ]