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,729 @@
1
+ # Copyright (c) Gateway. All rights reserved.
2
+
3
+ """Mapping engine for JSON push-to-trace.
4
+
5
+ Pure data transformation -- no OTel dependency. Handles:
6
+ - Dot-path resolution through nested dicts
7
+ - Convention-based auto-detection (snake_case + camelCase)
8
+ - Dict-to-TraceMapping normalization
9
+ - Field merge strategy (explicit > mapped > context > defaults)
10
+ - Timestamp coercion to nanoseconds
11
+ - Mapping validation with structured errors
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import re
18
+ from datetime import datetime, timezone
19
+ from typing import Any, Dict, List, Optional, Tuple
20
+
21
+ from .push_models import (
22
+ ChildMapping,
23
+ TraceMapping,
24
+ ValidationIssue,
25
+ )
26
+
27
+ # =============================================================================
28
+ # Convention Tables
29
+ # =============================================================================
30
+
31
+ # Each list is checked in order; first match wins.
32
+ # Both snake_case and camelCase variants are included.
33
+
34
+ TRACE_NAME_KEYS = [
35
+ "name",
36
+ "trace_name",
37
+ "traceName",
38
+ "agent_name",
39
+ "agentName",
40
+ "task_name",
41
+ "taskName",
42
+ "workflow_name",
43
+ "workflowName",
44
+ ]
45
+
46
+ TRACE_INPUT_KEYS = [
47
+ "input",
48
+ "query",
49
+ "question",
50
+ "prompt",
51
+ "request",
52
+ "user_input",
53
+ "userInput",
54
+ "user_message",
55
+ "userMessage",
56
+ ]
57
+
58
+ TRACE_OUTPUT_KEYS = [
59
+ "output",
60
+ "result",
61
+ "answer",
62
+ "response",
63
+ "completion",
64
+ "final_answer",
65
+ "finalAnswer",
66
+ "agent_output",
67
+ "agentOutput",
68
+ ]
69
+
70
+ TRACE_METADATA_KEYS = [
71
+ "metadata",
72
+ "config",
73
+ "configuration",
74
+ "params",
75
+ "parameters",
76
+ "context",
77
+ ]
78
+
79
+ TRACE_TAGS_KEYS = ["tags", "labels", "categories"]
80
+
81
+ TRACE_SESSION_ID_KEYS = [
82
+ "session_id",
83
+ "sessionId",
84
+ "session",
85
+ "conversation_id",
86
+ "conversationId",
87
+ "thread_id",
88
+ "threadId",
89
+ ]
90
+
91
+ TRACE_USER_ID_KEYS = [
92
+ "user_id",
93
+ "userId",
94
+ "user",
95
+ "author",
96
+ ]
97
+
98
+ TRACE_TIMESTAMP_KEYS = [
99
+ "timestamp",
100
+ "created_at",
101
+ "createdAt",
102
+ "start_time",
103
+ "startTime",
104
+ "time",
105
+ "ts",
106
+ ]
107
+
108
+ CHILDREN_KEYS = [
109
+ "steps",
110
+ "spans",
111
+ "observations",
112
+ "events",
113
+ "actions",
114
+ "calls",
115
+ "messages",
116
+ "tool_calls",
117
+ "toolCalls",
118
+ "children",
119
+ "traces",
120
+ ]
121
+
122
+ # Per-child conventions (in addition to the trace-level ones above)
123
+ CHILD_TYPE_KEYS = [
124
+ "type",
125
+ "kind",
126
+ "span_kind",
127
+ "spanKind",
128
+ "observation_type",
129
+ "observationType",
130
+ ]
131
+
132
+ CHILD_MODEL_KEYS = [
133
+ "model",
134
+ "model_name",
135
+ "modelName",
136
+ "llm",
137
+ "model_id",
138
+ "modelId",
139
+ ]
140
+
141
+ CHILD_START_TIME_KEYS = [
142
+ "start_time",
143
+ "startTime",
144
+ "started_at",
145
+ "startedAt",
146
+ "timestamp",
147
+ "begin",
148
+ ]
149
+
150
+ CHILD_END_TIME_KEYS = [
151
+ "end_time",
152
+ "endTime",
153
+ "ended_at",
154
+ "endedAt",
155
+ "finished_at",
156
+ "finishedAt",
157
+ "completed_at",
158
+ "completedAt",
159
+ ]
160
+
161
+ CHILD_LEVEL_KEYS = ["level", "severity", "status"]
162
+
163
+ # Trace-level convention map: target_field -> candidate keys
164
+ TRACE_CONVENTIONS = {
165
+ "name": TRACE_NAME_KEYS,
166
+ "input": TRACE_INPUT_KEYS,
167
+ "output": TRACE_OUTPUT_KEYS,
168
+ "metadata": TRACE_METADATA_KEYS,
169
+ "tags": TRACE_TAGS_KEYS,
170
+ "session_id": TRACE_SESSION_ID_KEYS,
171
+ "user_id": TRACE_USER_ID_KEYS,
172
+ "timestamp": TRACE_TIMESTAMP_KEYS,
173
+ }
174
+
175
+ # Child-level convention map (extends trace-level for shared fields)
176
+ CHILD_CONVENTIONS = {
177
+ "name": TRACE_NAME_KEYS,
178
+ "input": TRACE_INPUT_KEYS,
179
+ "output": TRACE_OUTPUT_KEYS,
180
+ "metadata": TRACE_METADATA_KEYS,
181
+ "type": CHILD_TYPE_KEYS,
182
+ "model": CHILD_MODEL_KEYS,
183
+ "start_time": CHILD_START_TIME_KEYS,
184
+ "end_time": CHILD_END_TIME_KEYS,
185
+ "level": CHILD_LEVEL_KEYS,
186
+ }
187
+
188
+
189
+ # =============================================================================
190
+ # Path Resolution
191
+ # =============================================================================
192
+
193
+
194
+ def resolve_path(data: dict, dot_path: str) -> Tuple[bool, Any]:
195
+ """Navigate a dot-path string through nested dicts.
196
+
197
+ Returns (found, value). Does not raise on missing keys.
198
+
199
+ Examples::
200
+
201
+ resolve_path({"a": {"b": 1}}, "a.b") -> (True, 1)
202
+ resolve_path({"a": 1}, "a.b.c") -> (False, None)
203
+ """
204
+ current = data
205
+ for key in dot_path.split("."):
206
+ if isinstance(current, dict) and key in current:
207
+ current = current[key]
208
+ else:
209
+ return False, None
210
+ return True, current
211
+
212
+
213
+ # =============================================================================
214
+ # Convention Detection
215
+ # =============================================================================
216
+
217
+
218
+ def _find_convention_match(data: dict, candidate_keys: List[str]) -> Optional[str]:
219
+ """Find the first candidate key that exists in data (case-insensitive)."""
220
+ data_keys_lower = {k.lower(): k for k in data.keys()}
221
+ for candidate in candidate_keys:
222
+ actual_key = data_keys_lower.get(candidate.lower())
223
+ if actual_key is not None:
224
+ return actual_key
225
+ return None
226
+
227
+
228
+ def apply_conventions(data: dict, depth: Optional[int] = 1) -> TraceMapping:
229
+ """Scan JSON keys for convention matches and return a TraceMapping.
230
+
231
+ Args:
232
+ data: The JSON dict to scan.
233
+ depth: How deep to recurse for children conventions.
234
+ 0 = trace-level only, 1 = one level of children, None = unlimited.
235
+ """
236
+ kwargs: Dict[str, Any] = {}
237
+
238
+ for target, candidates in TRACE_CONVENTIONS.items():
239
+ match = _find_convention_match(data, candidates)
240
+ if match is not None:
241
+ kwargs[target] = match
242
+
243
+ # Detect children array
244
+ if depth is None or depth > 0:
245
+ children_key = _find_convention_match(data, CHILDREN_KEYS)
246
+ if children_key is not None:
247
+ found, children_data = resolve_path(data, children_key)
248
+ if found and isinstance(children_data, list) and children_data:
249
+ child_kwargs: Dict[str, Any] = {"path": children_key}
250
+ sample = children_data[0]
251
+ if isinstance(sample, dict):
252
+ for target, candidates in CHILD_CONVENTIONS.items():
253
+ match = _find_convention_match(sample, candidates)
254
+ if match is not None:
255
+ child_kwargs[target] = match
256
+ kwargs["children"] = ChildMapping(**child_kwargs)
257
+
258
+ return TraceMapping(**kwargs)
259
+
260
+
261
+ # =============================================================================
262
+ # Mapping Normalization
263
+ # =============================================================================
264
+
265
+
266
+ def normalize_mapping(raw: dict) -> Tuple[TraceMapping, List[str]]:
267
+ """Convert a flat dict-form mapping to a TraceMapping.
268
+
269
+ Accepts keys like "trace.name", "trace.input", "children", "children.name".
270
+
271
+ Returns:
272
+ (mapping, unrecognized_keys) -- the mapping and any keys that weren't
273
+ recognized. Unrecognized keys likely indicate typos.
274
+ """
275
+ trace_kwargs: Dict[str, Any] = {}
276
+ child_kwargs: Dict[str, Any] = {}
277
+ unrecognized: List[str] = []
278
+
279
+ for key, value in raw.items():
280
+ if key.startswith("trace."):
281
+ field = key[len("trace.") :]
282
+ trace_kwargs[field] = value
283
+ elif key == "children":
284
+ child_kwargs["path"] = value
285
+ elif key.startswith("children."):
286
+ field = key[len("children.") :]
287
+ child_kwargs[field] = value
288
+ else:
289
+ unrecognized.append(key)
290
+
291
+ if child_kwargs and "path" in child_kwargs:
292
+ trace_kwargs["children"] = ChildMapping(**child_kwargs)
293
+
294
+ return TraceMapping(**trace_kwargs), unrecognized
295
+
296
+
297
+ # =============================================================================
298
+ # Timestamp Coercion
299
+ # =============================================================================
300
+
301
+ _NANOS_PER_SECOND = 1_000_000_000
302
+ _NANOS_PER_MILLI = 1_000_000
303
+ _NANOS_PER_MICRO = 1_000
304
+
305
+ # Thresholds for distinguishing epoch units
306
+ _SECONDS_THRESHOLD = 10**12
307
+ _MILLIS_THRESHOLD = 10**15
308
+ _MICROS_THRESHOLD = 10**18
309
+
310
+ _UNIT_MULTIPLIERS = {
311
+ "s": _NANOS_PER_SECOND,
312
+ "ms": _NANOS_PER_MILLI,
313
+ "us": _NANOS_PER_MICRO,
314
+ "ns": 1,
315
+ }
316
+
317
+
318
+ def coerce_timestamp(value: Any, unit: str = "auto") -> Optional[int]:
319
+ """Convert a timestamp value to nanoseconds since epoch.
320
+
321
+ Args:
322
+ value: The timestamp value to convert.
323
+ unit: Explicit unit for numeric values. One of "s", "ms", "us", "ns",
324
+ or "auto" (default). When "auto", the unit is inferred from
325
+ the magnitude of the value. Use an explicit unit to avoid
326
+ magnitude-based guessing.
327
+
328
+ Accepts:
329
+ - int/float: epoch seconds, milliseconds, microseconds, or nanoseconds
330
+ - str: ISO 8601 format
331
+ - datetime: Python datetime object
332
+
333
+ Returns None if the value cannot be coerced.
334
+ """
335
+ if value is None:
336
+ return None
337
+
338
+ if isinstance(value, datetime):
339
+ if value.tzinfo is None:
340
+ value = value.replace(tzinfo=timezone.utc)
341
+ return int(value.timestamp() * _NANOS_PER_SECOND)
342
+
343
+ if isinstance(value, (int, float)):
344
+ if unit != "auto":
345
+ multiplier = _UNIT_MULTIPLIERS.get(unit)
346
+ if multiplier is None:
347
+ return None
348
+ return int(value * multiplier)
349
+
350
+ # Auto: infer unit from magnitude.
351
+ # Compare as int when possible to avoid float64 precision loss
352
+ # at the microsecond/nanosecond boundary (~1e18).
353
+ magnitude = value if isinstance(value, int) else float(value)
354
+ if magnitude < _SECONDS_THRESHOLD:
355
+ return int(value * _NANOS_PER_SECOND)
356
+ if magnitude < _MILLIS_THRESHOLD:
357
+ return int(value * _NANOS_PER_MILLI)
358
+ if magnitude < _MICROS_THRESHOLD:
359
+ return int(value * _NANOS_PER_MICRO)
360
+ return int(value)
361
+
362
+ if isinstance(value, str):
363
+ try:
364
+ dt = datetime.fromisoformat(value.replace("Z", "+00:00"))
365
+ if dt.tzinfo is None:
366
+ dt = dt.replace(tzinfo=timezone.utc)
367
+ return int(dt.timestamp() * _NANOS_PER_SECOND)
368
+ except (ValueError, TypeError):
369
+ return None
370
+
371
+ return None
372
+
373
+
374
+ # =============================================================================
375
+ # Field Merge
376
+ # =============================================================================
377
+
378
+
379
+ def merge_fields(
380
+ mapped: Dict[str, Any],
381
+ explicit: Dict[str, Any],
382
+ context: Dict[str, Any],
383
+ defaults: Dict[str, Any],
384
+ ) -> Dict[str, Any]:
385
+ """Apply the merge strategy across priority levels.
386
+
387
+ Priority (highest to lowest):
388
+ 1. explicit -- push() keyword arguments
389
+ 2. mapped -- values extracted from JSON via mapping
390
+ 3. context -- ambient tracing_context() / run context
391
+ 4. defaults -- from tracing.init()
392
+
393
+ Special rules:
394
+ - tags: union, deduplicated
395
+ - metadata: deep merge, higher priority wins on key conflict
396
+ """
397
+ result: Dict[str, Any] = {}
398
+ all_sources = [defaults, context, mapped, explicit]
399
+
400
+ # Scalar fields: highest priority wins
401
+ scalar_fields = [
402
+ "name",
403
+ "session_id",
404
+ "user_id",
405
+ "run_id",
406
+ "environment",
407
+ "input",
408
+ "output",
409
+ "timestamp",
410
+ ]
411
+ for field in scalar_fields:
412
+ for source in reversed(all_sources):
413
+ if field in source and source[field] is not None:
414
+ result[field] = source[field]
415
+ break
416
+
417
+ # Tags: union across all sources, deduplicated
418
+ all_tags: List[str] = []
419
+ for source in all_sources:
420
+ tags = source.get("tags")
421
+ if isinstance(tags, list):
422
+ all_tags.extend(tags)
423
+ if all_tags:
424
+ result["tags"] = list(dict.fromkeys(all_tags)) # preserves order
425
+
426
+ # Metadata: deep merge, higher priority wins on conflict
427
+ merged_meta: Dict[str, Any] = {}
428
+ for source in all_sources:
429
+ meta = source.get("metadata")
430
+ if isinstance(meta, dict):
431
+ merged_meta.update(meta)
432
+ if merged_meta:
433
+ result["metadata"] = merged_meta
434
+
435
+ return result
436
+
437
+
438
+ # =============================================================================
439
+ # Mapping Validation
440
+ # =============================================================================
441
+
442
+ # Expected types for each mappable field
443
+ _EXPECTED_TYPES = {
444
+ "name": "str",
445
+ "input": "json_serializable",
446
+ "output": "json_serializable",
447
+ "metadata": "dict",
448
+ "tags": "list",
449
+ "session_id": "str",
450
+ "user_id": "str",
451
+ "run_id": "str",
452
+ "timestamp": "timestamp",
453
+ "environment": "str",
454
+ }
455
+
456
+ _CHILD_EXPECTED_TYPES = {
457
+ "name": "str",
458
+ "input": "json_serializable",
459
+ "output": "json_serializable",
460
+ "metadata": "dict",
461
+ "type": "str",
462
+ "model": "str",
463
+ "level": "str",
464
+ "start_time": "timestamp",
465
+ "end_time": "timestamp",
466
+ "usage": "dict",
467
+ "cost": "dict",
468
+ "model_parameters": "dict",
469
+ "status_message": "str",
470
+ "prompt_name": "str",
471
+ "prompt_version": "number",
472
+ "attributes": "dict",
473
+ }
474
+
475
+ _VALID_OBSERVATION_TYPES = {
476
+ "SPAN",
477
+ "GENERATION",
478
+ "TOOL",
479
+ "AGENT",
480
+ "CHAIN",
481
+ "RETRIEVER",
482
+ "EMBEDDING",
483
+ "EVALUATOR",
484
+ "GUARDRAIL",
485
+ "EVENT",
486
+ # Also accept lowercase
487
+ "span",
488
+ "generation",
489
+ "tool",
490
+ "agent",
491
+ "chain",
492
+ "retriever",
493
+ "embedding",
494
+ "evaluator",
495
+ "guardrail",
496
+ "event",
497
+ }
498
+
499
+ _VALID_LEVELS = {"DEBUG", "DEFAULT", "WARNING", "ERROR"}
500
+
501
+
502
+ def _is_json_serializable(value: Any) -> bool:
503
+ """Check if a value is JSON-serializable (str, dict, list, number, bool, None)."""
504
+ return isinstance(value, (str, int, float, bool, dict, list, type(None)))
505
+
506
+
507
+ def _check_type(value: Any, expected: str) -> bool:
508
+ """Check if a value matches the expected type category."""
509
+ if expected == "str":
510
+ return isinstance(value, str)
511
+ if expected == "dict":
512
+ return isinstance(value, dict)
513
+ if expected == "list":
514
+ return isinstance(value, list)
515
+ if expected == "number":
516
+ return isinstance(value, (int, float))
517
+ if expected == "json_serializable":
518
+ return _is_json_serializable(value)
519
+ if expected == "timestamp":
520
+ return coerce_timestamp(value) is not None
521
+ return True
522
+
523
+
524
+ def validate_mapping(
525
+ data: dict,
526
+ mapping: TraceMapping,
527
+ ) -> Tuple[List[ValidationIssue], List[ValidationIssue]]:
528
+ """Validate that all mapping paths resolve and types match.
529
+
530
+ Returns (errors, warnings).
531
+ """
532
+ errors: List[ValidationIssue] = []
533
+ warnings: List[ValidationIssue] = []
534
+
535
+ def _available_keys(d: dict) -> List[str]:
536
+ return list(d.keys())[:20]
537
+
538
+ # Validate trace-level fields
539
+ for field, expected in _EXPECTED_TYPES.items():
540
+ dot_path = getattr(mapping, field, None)
541
+ if dot_path is None:
542
+ continue
543
+
544
+ found, value = resolve_path(data, dot_path)
545
+ if not found:
546
+ issue = ValidationIssue(
547
+ field=f"trace.{field}",
548
+ path=dot_path,
549
+ issue="path_not_found",
550
+ message=f"Mapping path '{dot_path}' not found in data.",
551
+ available_keys=_available_keys(data),
552
+ )
553
+ if field in ("name", "input", "output"):
554
+ errors.append(issue)
555
+ else:
556
+ warnings.append(issue)
557
+ continue
558
+
559
+ if not _check_type(value, expected):
560
+ errors.append(
561
+ ValidationIssue(
562
+ field=f"trace.{field}",
563
+ path=dot_path,
564
+ issue="type_mismatch",
565
+ message=f"Field 'trace.{field}' resolved to type {type(value).__name__}, expected {expected}.",
566
+ expected_type=expected,
567
+ actual_type=type(value).__name__,
568
+ )
569
+ )
570
+
571
+ # Validate children
572
+ if mapping.children is not None:
573
+ _validate_children(data, mapping.children, errors, warnings)
574
+
575
+ return errors, warnings
576
+
577
+
578
+ def _validate_children(
579
+ data: dict,
580
+ child_mapping: ChildMapping,
581
+ errors: List[ValidationIssue],
582
+ warnings: List[ValidationIssue],
583
+ ) -> None:
584
+ """Validate children mapping paths against the data."""
585
+ found, children_data = resolve_path(data, child_mapping.path)
586
+
587
+ if not found:
588
+ errors.append(
589
+ ValidationIssue(
590
+ field="children",
591
+ path=child_mapping.path,
592
+ issue="path_not_found",
593
+ message=f"Children path '{child_mapping.path}' not found in data.",
594
+ available_keys=list(data.keys())[:20],
595
+ )
596
+ )
597
+ return
598
+
599
+ if not isinstance(children_data, list):
600
+ errors.append(
601
+ ValidationIssue(
602
+ field="children",
603
+ path=child_mapping.path,
604
+ issue="type_mismatch",
605
+ message=f"Children path '{child_mapping.path}' resolved to {type(children_data).__name__}, expected list.",
606
+ expected_type="list",
607
+ actual_type=type(children_data).__name__,
608
+ )
609
+ )
610
+ return
611
+
612
+ if not children_data:
613
+ return
614
+
615
+ # Validate against first child as sample
616
+ sample = children_data[0]
617
+ if not isinstance(sample, dict):
618
+ return
619
+
620
+ for field, expected in _CHILD_EXPECTED_TYPES.items():
621
+ dot_path = getattr(child_mapping, field, None)
622
+ if dot_path is None:
623
+ continue
624
+
625
+ found, value = resolve_path(sample, dot_path)
626
+ if not found:
627
+ warnings.append(
628
+ ValidationIssue(
629
+ field=f"children.{field}",
630
+ path=dot_path,
631
+ issue="path_not_found",
632
+ message=f"Mapping path '{dot_path}' not found in children[0].",
633
+ available_keys=list(sample.keys())[:20],
634
+ )
635
+ )
636
+ continue
637
+
638
+ if not _check_type(value, expected):
639
+ warnings.append(
640
+ ValidationIssue(
641
+ field=f"children.{field}",
642
+ path=dot_path,
643
+ issue="type_mismatch",
644
+ message=f"Field 'children.{field}' resolved to type {type(value).__name__}, expected {expected}.",
645
+ expected_type=expected,
646
+ actual_type=type(value).__name__,
647
+ )
648
+ )
649
+
650
+ # Extra validation for constrained string fields
651
+ if field == "type" and isinstance(value, str) and value not in _VALID_OBSERVATION_TYPES:
652
+ warnings.append(
653
+ ValidationIssue(
654
+ field="children.type",
655
+ path=dot_path,
656
+ issue="invalid_type_value",
657
+ message=f"Observation type '{value}' not recognized. Valid: SPAN, GENERATION, TOOL, AGENT, etc.",
658
+ )
659
+ )
660
+
661
+ if field == "level" and isinstance(value, str) and value.upper() not in _VALID_LEVELS:
662
+ warnings.append(
663
+ ValidationIssue(
664
+ field="children.level",
665
+ path=dot_path,
666
+ issue="invalid_type_value",
667
+ message=f"Level '{value}' not recognized. Valid: DEBUG, DEFAULT, WARNING, ERROR.",
668
+ )
669
+ )
670
+
671
+
672
+ # =============================================================================
673
+ # Inspection Helpers
674
+ # =============================================================================
675
+
676
+ # Heuristic suggestions for unmatched keys
677
+ _SUGGESTION_PATTERNS: List[Tuple[str, str, re.Pattern]] = [
678
+ ("trace.output", "root", re.compile(r"(final|agent|llm).*(answer|output|result|response)", re.I)),
679
+ ("trace.input", "root", re.compile(r"(user|agent|llm).*(input|query|question|prompt)", re.I)),
680
+ ("children.input", "child", re.compile(r"(step|action|tool).*(input|query)", re.I)),
681
+ ("children.output", "child", re.compile(r"(step|action|tool).*(output|result|response)", re.I)),
682
+ ("children.model", "child", re.compile(r"(model|llm).*(used|name|id)", re.I)),
683
+ ]
684
+
685
+
686
+ def suggest_mapping_for_key(key: str, level: str) -> Optional[str]:
687
+ """Try to suggest a mapping target for an unrecognized key using heuristics."""
688
+ for target, target_level, pattern in _SUGGESTION_PATTERNS:
689
+ if target_level == level and pattern.search(key):
690
+ return target
691
+
692
+ # Simple suffix heuristics
693
+ lower = key.lower()
694
+ if level == "child":
695
+ if lower.endswith("_input") or lower.endswith("input"):
696
+ return "children.input"
697
+ if lower.endswith("_output") or lower.endswith("output"):
698
+ return "children.output"
699
+ if lower.endswith("_result") or lower.endswith("result"):
700
+ return "children.output"
701
+
702
+ return None
703
+
704
+
705
+ def describe_type(value: Any) -> str:
706
+ """Return a human-readable type description for a value."""
707
+ if isinstance(value, list):
708
+ if value and isinstance(value[0], dict):
709
+ return "list[dict]"
710
+ if value and isinstance(value[0], str):
711
+ return "list[str]"
712
+ return "list"
713
+ return type(value).__name__
714
+
715
+
716
+ def truncate_sample(value: Any, max_len: int = 60) -> str:
717
+ """Return a truncated string representation of a value."""
718
+ if isinstance(value, str):
719
+ if len(value) > max_len:
720
+ return json.dumps(value[:max_len] + "...")
721
+ return json.dumps(value)
722
+ if isinstance(value, dict):
723
+ s = json.dumps(value)
724
+ if len(s) > max_len:
725
+ return s[:max_len] + "..."
726
+ return s
727
+ if isinstance(value, list):
728
+ return f"({len(value)} items)"
729
+ return str(value)