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,615 @@
1
+ """One authored authority -> deterministic SQLite, runtime, docs and tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ try:
12
+ import tomllib
13
+ except ModuleNotFoundError:
14
+ import tomli as tomllib
15
+
16
+ from .validation import (
17
+ LANGUAGE,
18
+ ContractError,
19
+ Resolver,
20
+ canonical,
21
+ digest,
22
+ encoded,
23
+ fail,
24
+ members,
25
+ nonempty,
26
+ publication_digest,
27
+ )
28
+
29
+ COMPILER = "gateway-world-compiler/1"
30
+ TARGET = "sqlite-json/1"
31
+ GENERATED = (
32
+ "db/schema.sql",
33
+ "schema/runtime.json",
34
+ "schema/README.md",
35
+ "tool-schemas.json",
36
+ )
37
+ LOCK = "schema/lock.json"
38
+ ROOT = "schema/world.json"
39
+ RESERVED = ("_", "sqlite_", "gateway_", "benchmark_")
40
+ FIELD_RESERVED = ("sqlite_", "gateway_", "benchmark_")
41
+
42
+
43
+ def identifier(value: Any, location: str) -> str:
44
+ if (
45
+ not isinstance(value, str)
46
+ or not re.fullmatch(r"[A-Za-z][A-Za-z0-9_]*", value)
47
+ or value.lower().startswith(RESERVED)
48
+ ):
49
+ fail(
50
+ "identity",
51
+ "Use a SQL-safe name; _, sqlite_, gateway_, benchmark_ prefixes are reserved",
52
+ location,
53
+ )
54
+ return value
55
+
56
+
57
+ def field_identifier(value: Any, location: str) -> str:
58
+ if (
59
+ not isinstance(value, str)
60
+ or not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", value)
61
+ or value.lower() in {"_key", "payload"}
62
+ or value.lower().startswith(FIELD_RESERVED)
63
+ ):
64
+ fail(
65
+ "identity",
66
+ "Use a SQL-safe payload name; _key, payload, sqlite_, gateway_, and benchmark_ are reserved",
67
+ location,
68
+ )
69
+ return value
70
+
71
+
72
+ def quote(value: str) -> str:
73
+ return '"' + value.replace('"', '""') + '"'
74
+
75
+
76
+ def json_pointer_parts(value: str, location: str, *, code: str) -> list[str]:
77
+ if not value.startswith("/"):
78
+ return [value]
79
+ parts = []
80
+ for part in value[1:].split("/"):
81
+ if re.search(r"~(?![01])", part):
82
+ fail(code, "Invalid JSON Pointer escape in field path", location)
83
+ decoded = part.replace("~1", "/").replace("~0", "~")
84
+ if '"' in decoded:
85
+ fail(code, "Quoted property names are unsupported in key paths", location)
86
+ parts.append(decoded)
87
+ return parts
88
+
89
+
90
+ def field_schema(
91
+ schema: dict,
92
+ field: str,
93
+ location: str,
94
+ *,
95
+ code: str = "key",
96
+ nested: bool = True,
97
+ ) -> dict:
98
+ if field.startswith("/") and not nested:
99
+ fail(code, "Primary keys must use direct property names", location)
100
+ parts = json_pointer_parts(field, location, code=code)
101
+
102
+ def resolve(current: dict, remaining: list[str]) -> dict | None:
103
+ if not remaining:
104
+ return current
105
+ part, *rest = remaining
106
+ if current.get("type") == "object" and part in current.get("properties", {}):
107
+ return resolve(current["properties"][part], rest)
108
+ for keyword in ("allOf", "anyOf", "oneOf"):
109
+ matches = [
110
+ match for branch in current.get(keyword, []) if (match := resolve(branch, remaining)) is not None
111
+ ]
112
+ if len(matches) == 1 or (matches and all(canonical(match) == canonical(matches[0]) for match in matches)):
113
+ return matches[0]
114
+ return None
115
+
116
+ resolved = resolve(schema, parts)
117
+ if resolved is None:
118
+ fail(code, "Field paths must resolve through object properties", location)
119
+ return resolved
120
+
121
+
122
+ def payload_value(value: dict, field: str) -> Any:
123
+ current: Any = value
124
+ for part in json_pointer_parts(field, field, code="key"):
125
+ if not isinstance(current, dict) or part not in current:
126
+ return None
127
+ current = current[part]
128
+ return current
129
+
130
+
131
+ def _json_path(field: str) -> str:
132
+ parts = json_pointer_parts(field, field, code="key")
133
+ return "$" + "".join(f".{json.dumps(part, ensure_ascii=False)}" for part in parts)
134
+
135
+
136
+ def bundle_mode(bundle: Path) -> dict | None:
137
+ """Markers never downgrade to legacy when an authority has disappeared."""
138
+ bundle = Path(bundle)
139
+ path = bundle / "gateway-env.toml"
140
+ # exists() is False for a dangling symlink; a broken authority must fail,
141
+ # never quietly run as an unconstrained legacy bundle.
142
+ markers = any((bundle / p).exists() or (bundle / p).is_symlink() for p in (ROOT, LOCK, "schema/runtime.json"))
143
+ try:
144
+ manifest = tomllib.loads(path.read_text(encoding="utf-8")) if path.exists() else {}
145
+ except (OSError, ValueError) as error:
146
+ raise ContractError("manifest", "Cannot parse gateway-env.toml") from error
147
+ declaration = manifest.get("schema")
148
+ if declaration is None and not markers:
149
+ return None
150
+ if declaration != {"language": LANGUAGE}:
151
+ fail(
152
+ "language",
153
+ f"Schema bundles require exactly [schema] language = '{LANGUAGE}'",
154
+ "gateway-env.toml/schema",
155
+ )
156
+ if (bundle / "gateway-api-world.toml").exists():
157
+ fail("bundle_mode", "Schema contracts currently support mock-tools bundles only")
158
+ env = manifest.get("environment", manifest.get("env", {}))
159
+ if not isinstance(env, dict) or env.get("kind", "mock-tools") != "mock-tools":
160
+ fail("bundle_mode", "Schema contracts currently support mock-tools bundles only")
161
+ db = manifest.get("db", {})
162
+ if not isinstance(db, dict) or db.get("engine", "sqlite") != "sqlite" or env.get("db", "sqlite") != "sqlite":
163
+ fail("target", "Schema worlds require SQLite")
164
+ if any(k in db for k in ("schema", "seed", "path")):
165
+ fail(
166
+ "unsupported",
167
+ "Schema mode uses fixed generated/initial paths; remove db.schema/seed/path",
168
+ )
169
+ if (bundle / "db/seed.sql").exists():
170
+ fail(
171
+ "raw_seed",
172
+ "Remove db/seed.sql; schema worlds seed through data/initial.json",
173
+ )
174
+ for p in (ROOT, LOCK, *GENERATED, "data/initial.json"):
175
+ target = bundle / p
176
+ if target.is_symlink() or not target.resolve().is_relative_to(bundle.resolve()):
177
+ fail(
178
+ "path",
179
+ "Bundle contract paths may not escape the bundle or be symlinks",
180
+ p,
181
+ )
182
+ return manifest
183
+
184
+
185
+ def require_legacy_bundle(bundle: str | Path, runtime: str) -> None:
186
+ """Explicit rejection for alternate execution paths without store support."""
187
+ if bundle_mode(Path(bundle)) is not None:
188
+ fail(
189
+ "bundle_mode",
190
+ f"Schema-backed bundles are unsupported by {runtime}; use the SDK environment or mock-tools runner",
191
+ )
192
+
193
+
194
+ def _key_fields(
195
+ fields: Any,
196
+ schema: dict,
197
+ location: str,
198
+ *,
199
+ required: bool = False,
200
+ nested: bool = True,
201
+ code: str = "key",
202
+ ) -> list[str]:
203
+ if (
204
+ not isinstance(fields, list)
205
+ or not fields
206
+ or any(not isinstance(k, str) for k in fields)
207
+ or len(set(fields)) != len(fields)
208
+ ):
209
+ fail(code, "Keys require a nonempty list of distinct property paths", location)
210
+ for field in fields:
211
+ prop = field_schema(schema, field, f"{location}/{field}", code=code, nested=nested)
212
+ types = prop.get("type", [])
213
+ types = [types] if isinstance(types, str) else types
214
+ if not set(types) or set(types) - {"string", "integer", "null"} or not set(types) & {"string", "integer"}:
215
+ fail(code, "Key paths must resolve to string/integer properties", location)
216
+ if any(k in prop for k in ("allOf", "oneOf", "anyOf")):
217
+ fail(code, "Composition is not supported on key fields", location)
218
+ if required and (field not in schema.get("required", []) or "null" in types):
219
+ fail(
220
+ code,
221
+ "Primary-key fields must be required and non-null",
222
+ f"{location}/{field}",
223
+ )
224
+ return fields
225
+
226
+
227
+ @dataclass(frozen=True)
228
+ class Contract:
229
+ metadata: dict
230
+ artifacts: dict[str, bytes]
231
+ lock: dict
232
+
233
+ @property
234
+ def target(self) -> dict:
235
+ return self.metadata["domain"]
236
+
237
+ @property
238
+ def entities(self) -> dict:
239
+ return self.metadata["entities"]
240
+
241
+ @property
242
+ def tools(self) -> dict:
243
+ return self.metadata["tools"]
244
+
245
+
246
+ def build(bundle: Path) -> Contract:
247
+ bundle = Path(bundle)
248
+ manifest = bundle_mode(bundle)
249
+ if manifest is None:
250
+ fail("language", "Not a schema-backed bundle; declare [schema] language")
251
+ resolver = Resolver(bundle)
252
+ document = resolver.load("world.json")
253
+ annotation = document.get("x-gateway")
254
+ if not isinstance(annotation, dict):
255
+ fail("schema", "x-gateway must be an object", "world.json#/x-gateway")
256
+ members(
257
+ annotation,
258
+ {"version", "domain_version", "package", "entities", "tools"},
259
+ {"version", "domain_version", "package", "entities", "tools"},
260
+ "world.json#/x-gateway",
261
+ )
262
+ if type(annotation["version"]) is not int or annotation["version"] != 1:
263
+ fail(
264
+ "language",
265
+ "Only x-gateway version 1 is supported",
266
+ "world.json#/x-gateway/version",
267
+ )
268
+ nonempty(annotation["domain_version"], "world.json#/x-gateway/domain_version")
269
+ package = annotation["package"]
270
+ if not isinstance(package, dict):
271
+ fail("schema", "package must be an object", "world.json#/x-gateway/package")
272
+ members(package, {"id", "version"}, {"id", "version"}, "world.json#/x-gateway/package")
273
+ for key, value in package.items():
274
+ nonempty(value, f"world.json#/x-gateway/package/{key}")
275
+ entities = annotation["entities"]
276
+ if not isinstance(entities, dict) or not entities:
277
+ fail("schema", "Declare at least one entity", "world.json#/x-gateway/entities")
278
+ compiled = {}
279
+ seen = set()
280
+ for name, entity in sorted(entities.items()):
281
+ location = f"world.json#/x-gateway/entities/{name}"
282
+ identifier(name, location)
283
+ if name.lower() in seen:
284
+ fail("identity", "Entity names collide under SQLite case folding", location)
285
+ seen.add(name.lower())
286
+ members(
287
+ entity,
288
+ {"schema", "primary_key", "unique", "relationships"},
289
+ {"schema", "primary_key"},
290
+ location,
291
+ )
292
+ payload = resolver.resolve(entity["schema"], "world.json", f"{location}/schema")
293
+ if (
294
+ payload.get("type") != "object"
295
+ or payload.get("additionalProperties") is not False
296
+ or any(k in payload for k in ("allOf", "oneOf", "anyOf"))
297
+ ):
298
+ fail(
299
+ "entity",
300
+ "Entity roots must be direct closed objects (reuse through $ref, no inheritance)",
301
+ location,
302
+ )
303
+ field_names = set()
304
+ for field in payload.get("properties", {}):
305
+ field_identifier(field, f"{location}/schema/properties/{field}")
306
+ if field.lower() in {"payload"} | field_names:
307
+ fail(
308
+ "identity",
309
+ "Payload field collides with storage or another field",
310
+ location,
311
+ )
312
+ field_names.add(field.lower())
313
+ primary = _key_fields(
314
+ entity["primary_key"],
315
+ payload,
316
+ f"{location}/primary_key",
317
+ required=True,
318
+ nested=False,
319
+ )
320
+ unique = entity.get("unique", [])
321
+ if not isinstance(unique, list):
322
+ fail("key", "unique must be a list of key lists", location)
323
+ constraints = {frozenset(primary)}
324
+ for key in unique:
325
+ _key_fields(key, payload, f"{location}/unique")
326
+ if frozenset(key) in constraints:
327
+ fail("key", "Duplicate key declaration", location)
328
+ constraints.add(frozenset(key))
329
+ relationships = entity.get("relationships", [])
330
+ if not isinstance(relationships, list):
331
+ fail("relationship", "relationships must be an array", location)
332
+ relation_fields = set()
333
+ for rel in relationships:
334
+ members(
335
+ rel,
336
+ {"fields", "target", "on_delete"},
337
+ {"fields", "target", "on_delete"},
338
+ f"{location}/relationships",
339
+ )
340
+ _key_fields(
341
+ rel["fields"],
342
+ payload,
343
+ f"{location}/relationships/fields",
344
+ code="relationship",
345
+ )
346
+ nonempty(rel["target"], f"{location}/relationships/target")
347
+ if rel["on_delete"] != "restrict":
348
+ fail(
349
+ "unsupported",
350
+ "Only restrict deletion is supported; delete dependents explicitly in one batch",
351
+ location,
352
+ )
353
+ if tuple(rel["fields"]) in relation_fields:
354
+ fail("relationship", "A field tuple may have only one target", location)
355
+ relation_fields.add(tuple(rel["fields"]))
356
+ compiled[name] = {
357
+ "schema": payload,
358
+ "primary_key": primary,
359
+ "unique": sorted(unique),
360
+ "relationships": sorted(relationships, key=canonical),
361
+ "location": location,
362
+ }
363
+ for name, entity in compiled.items():
364
+ for rel in entity["relationships"]:
365
+ location = f"{entity['location']}/relationships"
366
+ target = compiled.get(rel["target"])
367
+ if target is None or len(target["primary_key"]) != len(rel["fields"]):
368
+ fail(
369
+ "relationship",
370
+ "Target must exist with matching primary-key arity",
371
+ location,
372
+ )
373
+ for source, dest in zip(rel["fields"], target["primary_key"]):
374
+ source_type = field_schema(
375
+ entity["schema"],
376
+ source,
377
+ location,
378
+ code="relationship",
379
+ )["type"]
380
+ source_types = set(source_type if isinstance(source_type, list) else [source_type]) - {"null"}
381
+ target_type = target["schema"]["properties"][dest]["type"]
382
+ target_types = set(target_type if isinstance(target_type, list) else [target_type])
383
+ if source_types != target_types:
384
+ fail(
385
+ "relationship",
386
+ "Relationship endpoint key types must match",
387
+ location,
388
+ )
389
+ tools = annotation["tools"]
390
+ if not isinstance(tools, dict):
391
+ fail("tool", "tools must be an object", "world.json#/x-gateway/tools")
392
+ compiled_tools = {}
393
+ for name, tool in sorted(tools.items()):
394
+ location = f"world.json#/x-gateway/tools/{name}"
395
+ if not re.fullmatch(r"[A-Za-z][A-Za-z0-9_.-]*", name):
396
+ fail("tool", "Invalid tool name", location)
397
+ members(tool, {"input", "output", "description"}, {"input", "output"}, location)
398
+ if "description" in tool and not isinstance(tool["description"], str):
399
+ fail("tool", "Tool description must be text", location)
400
+ inputs = resolver.resolve(tool["input"], "world.json", f"{location}/input")
401
+ outputs = resolver.resolve(tool["output"], "world.json", f"{location}/output")
402
+ if inputs.get("type") != "object":
403
+ fail("tool", "Tool inputs must resolve to an object schema", location)
404
+ compiled_tools[name] = {
405
+ "input_schema": inputs,
406
+ "output_schema": outputs,
407
+ "description": tool.get("description", name),
408
+ }
409
+ served = manifest.get("tools", {}).get("serve") if isinstance(manifest.get("tools", {}), dict) else None
410
+ if (
411
+ not isinstance(served, list)
412
+ or any(type(n) is not str for n in served)
413
+ or len(served) != len(set(served))
414
+ or set(served) != set(tools)
415
+ ):
416
+ fail(
417
+ "tool",
418
+ "Manifest tools.serve must exactly match authored tool contracts",
419
+ "gateway-env.toml/tools/serve",
420
+ )
421
+ resolver.check_documents()
422
+ inputs = {
423
+ name: {
424
+ "id": doc["$id"],
425
+ "hash": digest(encoded(doc)),
426
+ "publication_hash": publication_digest(doc),
427
+ }
428
+ for name, doc in sorted(resolver.documents.items())
429
+ }
430
+ # Domain contract excludes package/tool behavior; changing a mapping cannot change it.
431
+ domain = {
432
+ "id": document["$id"],
433
+ "version": annotation["domain_version"],
434
+ "hash": digest(encoded({"language": LANGUAGE, "target": TARGET, "entities": compiled})),
435
+ }
436
+ metadata = {
437
+ "language": LANGUAGE,
438
+ "compiler": COMPILER,
439
+ "target": TARGET,
440
+ "domain": domain,
441
+ "package": package,
442
+ "entities": compiled,
443
+ "tools": compiled_tools,
444
+ }
445
+ sql = _ddl(compiled)
446
+ docs = [
447
+ f"# {domain['id']} ({domain['version']})",
448
+ "",
449
+ "Generated; edit schema/world.json and compile. Defaults are annotations only.",
450
+ "",
451
+ ]
452
+ for name, entity in compiled.items():
453
+ docs.extend(
454
+ [
455
+ f"## {name}",
456
+ "",
457
+ f"Primary key: {', '.join(entity['primary_key'])}",
458
+ "",
459
+ "| Property | Type | Required |",
460
+ "| --- | --- | --- |",
461
+ ]
462
+ )
463
+ for field, prop in sorted(entity["schema"].get("properties", {}).items()):
464
+ required = field in entity["schema"].get("required", [])
465
+ docs.append(f"| {field} | {canonical(prop.get('type', 'composition'))} | " f"{required} |")
466
+ docs.extend(
467
+ [
468
+ "",
469
+ f"Unique: {canonical(entity['unique'])}",
470
+ f"Relationships: {canonical(entity['relationships'])}",
471
+ "",
472
+ "```json",
473
+ json.dumps(entity["schema"], sort_keys=True, ensure_ascii=False, indent=2),
474
+ "```",
475
+ "",
476
+ ]
477
+ )
478
+ docs.extend(
479
+ [
480
+ "## Tool contracts",
481
+ "",
482
+ "```json",
483
+ json.dumps(compiled_tools, sort_keys=True, ensure_ascii=False, indent=2),
484
+ "```",
485
+ "",
486
+ ]
487
+ )
488
+ artifacts = {
489
+ "db/schema.sql": sql.encode(),
490
+ "schema/runtime.json": encoded(metadata),
491
+ "schema/README.md": "\n".join(docs).encode(),
492
+ "tool-schemas.json": encoded({"tools": compiled_tools}),
493
+ }
494
+ lock = {
495
+ "language": LANGUAGE,
496
+ "annotations": 1,
497
+ "compiler": COMPILER,
498
+ "target": TARGET,
499
+ "domain": domain,
500
+ "package": package,
501
+ "inputs": inputs,
502
+ "tools_hash": digest(encoded(compiled_tools)),
503
+ "artifacts": {p: digest(b) for p, b in sorted(artifacts.items())},
504
+ }
505
+ return Contract(metadata, artifacts, lock)
506
+
507
+
508
+ def _ddl(entities: dict) -> str:
509
+ lines = [
510
+ "-- Generated by gateway-world-compiler/1; do not edit.",
511
+ "PRAGMA foreign_keys = ON;",
512
+ ]
513
+ for name, entity in entities.items():
514
+ columns = [
515
+ '"_key" TEXT PRIMARY KEY NOT NULL',
516
+ '"payload" TEXT NOT NULL CHECK(json_valid("payload"))',
517
+ ]
518
+ for field, prop in sorted(entity["schema"].get("properties", {}).items()):
519
+ # JSON remains the source of truth. Projections make verifier SQL useful,
520
+ # and provide SQLite UNIQUE/FK enforcement without affinity coercion.
521
+ types = prop.get("type", [])
522
+ types = [types] if isinstance(types, str) else types
523
+ if set(types) & {"string", "integer", "number", "boolean"}:
524
+ columns.append(
525
+ f'{quote(field)} GENERATED ALWAYS AS (json_extract("payload", '
526
+ f"{quote_sql(_json_path(field))})) VIRTUAL"
527
+ )
528
+ projected = set(entity["schema"].get("properties", {}))
529
+ constrained = {field for fields in [entity["primary_key"], *entity["unique"]] for field in fields} | {
530
+ field for relationship in entity["relationships"] for field in relationship["fields"]
531
+ }
532
+ for field in sorted(constrained - projected):
533
+ columns.append(
534
+ f'{quote(field)} GENERATED ALWAYS AS (json_extract("payload", '
535
+ f"{quote_sql(_json_path(field))})) VIRTUAL"
536
+ )
537
+ for fields in [entity["primary_key"], *entity["unique"]]:
538
+ columns.append(f"UNIQUE ({', '.join(quote(f) for f in fields)})")
539
+ for rel in entity["relationships"]:
540
+ target = entities[rel["target"]]
541
+ columns.append(
542
+ f"FOREIGN KEY ({', '.join(quote(f) for f in rel['fields'])}) "
543
+ f"REFERENCES {quote(rel['target'])} "
544
+ f"({', '.join(quote(f) for f in target['primary_key'])}) "
545
+ "ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED"
546
+ )
547
+ lines.append(f"CREATE TABLE {quote(name)} (\n " + ",\n ".join(columns) + "\n);")
548
+ return "\n".join(lines) + "\n"
549
+
550
+
551
+ def quote_sql(value: str) -> str:
552
+ return "'" + value.replace("'", "''") + "'"
553
+
554
+
555
+ def compile_bundle(bundle: str | Path, *, snapshots: bool = True) -> Contract:
556
+ """Generate artifacts; never overwrite authored handlers or seed data.
557
+
558
+ With ``snapshots`` (the default) the data snapshots are built too, once the
559
+ tree has its ``data/initial.json``: ``db/state.sqlite`` and one per scenario,
560
+ each skipped when its sidecar already names this contract and batch. That
561
+ build is the proof the data applies through the contract. Data the contract
562
+ refuses (an author changed the model and has not re-ingested yet) leaves no
563
+ snapshot behind at all; the refusal is reported where the data is next used
564
+ (`gateway worlds schema compile` reports it at once, see ``compile_world``)."""
565
+ bundle = Path(bundle)
566
+ contract = build(bundle)
567
+ for path, content in {**contract.artifacts, LOCK: encoded(contract.lock)}.items():
568
+ destination = bundle / path
569
+ destination.parent.mkdir(parents=True, exist_ok=True)
570
+ destination.write_bytes(content)
571
+ if snapshots and (bundle / "data/initial.json").is_file():
572
+ from .snapshot import build_all, discard_all
573
+
574
+ try:
575
+ build_all(bundle, contract)
576
+ except ContractError:
577
+ discard_all(bundle)
578
+ return contract
579
+
580
+
581
+ def compile_world(bundle: str | Path) -> tuple[Contract, dict]:
582
+ """``compile_bundle`` plus the snapshots, refusing loudly when the data does not
583
+ apply through the contract: what the CLI runs, so an author sees the row."""
584
+ from .snapshot import build_all
585
+
586
+ contract = compile_bundle(bundle, snapshots=False)
587
+ return contract, build_all(bundle, contract)
588
+
589
+
590
+ def check_bundle(bundle: str | Path) -> Contract:
591
+ """The contract, provided every generated artifact and every snapshot present in
592
+ the tree matches it; a snapshot's sidecar must name this contract and the
593
+ file's own sha256. Absent snapshots are not required: the legacy path serves."""
594
+ bundle = Path(bundle)
595
+ contract = build(bundle)
596
+ for path, expected in {**contract.artifacts, LOCK: encoded(contract.lock)}.items():
597
+ try:
598
+ matches = (bundle / path).read_bytes() == expected
599
+ except OSError:
600
+ matches = False
601
+ if not matches:
602
+ fail(
603
+ "lock_mismatch",
604
+ "Missing, edited or incompatible contract artifact; explicitly "
605
+ "recompile (state migration is not automatic)",
606
+ path,
607
+ )
608
+ from .snapshot import check_all
609
+
610
+ check_all(bundle, contract)
611
+ return contract
612
+
613
+
614
+ def describe_bundle(bundle: str | Path) -> str:
615
+ return build(Path(bundle)).artifacts["schema/README.md"].decode()