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,550 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """Utilities shared for OpenTelemetry span (attributes) support."""
4
+
5
+ import json
6
+ import logging
7
+ import traceback
8
+ from typing import Any, Dict, List, Sequence, Type, TypeVar, Union, cast
9
+ from warnings import filterwarnings
10
+
11
+ import opentelemetry.trace as trace_api
12
+ from opentelemetry.sdk.trace import ReadableSpan, SpanLimits, SpanProcessor, SynchronousMultiSpanProcessor, Tracer
13
+ from opentelemetry.sdk.trace import TracerProvider as TracerProviderImpl
14
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
15
+ from opentelemetry.sdk.util.instrumentation import InstrumentationInfo, InstrumentationScope
16
+ from opentelemetry.semconv.attributes import exception_attributes
17
+ from opentelemetry.trace import get_tracer_provider as otel_get_tracer_provider
18
+ from pydantic import TypeAdapter
19
+
20
+ from gatewaysdk.env_var import LightningEnvVar, resolve_bool_env_var
21
+ from gatewaysdk.semconv import LightningSpanAttributes, LinkAttributes, LinkPydanticModel
22
+ from gatewaysdk.types import Attributes, AttributeValue, SpanLike
23
+ from gatewaysdk.utils.otlp import LightningStoreOTLPExporter
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ __all__ = [
28
+ "full_qualified_name",
29
+ "get_tracer_provider",
30
+ "get_tracer",
31
+ "make_tag_attributes",
32
+ "extract_tags_from_attributes",
33
+ "make_link_attributes",
34
+ "query_linked_spans",
35
+ "extract_links_from_attributes",
36
+ "filter_attributes",
37
+ "filter_and_unflatten_attributes",
38
+ "flatten_attributes",
39
+ "unflatten_attributes",
40
+ "sanitize_attribute_value",
41
+ "sanitize_attributes",
42
+ "sanitize_list_attribute_sanity",
43
+ "check_attributes_sanity",
44
+ "format_exception_attributes",
45
+ ]
46
+
47
+ T_SpanLike = TypeVar("T_SpanLike", bound=SpanLike)
48
+ T_SpanProcessor = TypeVar("T_SpanProcessor", bound=SpanProcessor)
49
+
50
+
51
+ def _is_agentops_exporter(exporter: object) -> bool:
52
+ """Check if exporter is an AgentOps OTLPSpanExporter without requiring the import."""
53
+ try:
54
+ from agentops.sdk.exporters import OTLPSpanExporter
55
+
56
+ return isinstance(exporter, OTLPSpanExporter)
57
+ except (ImportError, ModuleNotFoundError):
58
+ return False
59
+
60
+
61
+ def full_qualified_name(obj: type) -> str:
62
+ if str(obj.__module__) == "builtins":
63
+ return obj.__qualname__
64
+ return f"{obj.__module__}.{obj.__qualname__}"
65
+
66
+
67
+ def get_tracer_provider(inspect: bool = True) -> TracerProviderImpl:
68
+ """Get the OpenTelemetry tracer provider configured for GatewaySDK.
69
+
70
+ Args:
71
+ inspect: Whether to inspect the tracer provider and log its configuration.
72
+ When it's on, make sure you also set the logger level to DEBUG to see the logs.
73
+ """
74
+ from gatewaysdk.tracer.otel import LightningSpanProcessor
75
+
76
+ if hasattr(trace_api, "_TRACER_PROVIDER") and trace_api._TRACER_PROVIDER is None: # type: ignore[attr-defined]
77
+ raise RuntimeError("Tracer is not initialized. Cannot emit a meaningful span.")
78
+ tracer_provider = otel_get_tracer_provider()
79
+ if not isinstance(tracer_provider, TracerProviderImpl):
80
+ logger.error(
81
+ "Tracer provider is expected to be an instance of opentelemetry.sdk.trace.TracerProvider, found: %s",
82
+ full_qualified_name(type(tracer_provider)),
83
+ )
84
+ return cast(TracerProviderImpl, tracer_provider)
85
+
86
+ if not inspect:
87
+ return tracer_provider
88
+
89
+ emitter_debug = resolve_bool_env_var(LightningEnvVar.AGL_EMITTER_DEBUG, fallback=None)
90
+ logger_effective_level = logger.getEffectiveLevel()
91
+ if emitter_debug is True and logger_effective_level > logging.DEBUG:
92
+ logger.warning(
93
+ "Emitter debug logging is enabled but logging level is not set to DEBUG. Nothing will be logged."
94
+ )
95
+
96
+ if emitter_debug is None:
97
+ # Set to true by default if the logging level is lower than DEBUG
98
+ emitter_debug = logging.DEBUG >= logger_effective_level
99
+
100
+ if emitter_debug:
101
+ active_span_processor = tracer_provider._active_span_processor # pyright: ignore[reportPrivateUsage]
102
+ processors: List[str] = []
103
+ active_span_processor_cls = active_span_processor.__class__.__name__
104
+ for processor in active_span_processor._span_processors: # pyright: ignore[reportPrivateUsage]
105
+ if isinstance(processor, LightningSpanProcessor):
106
+ # The legacy case for tracers without OTLP support.
107
+ processors.append(f"{active_span_processor_cls} - {processor!r}")
108
+ elif isinstance(processor, (SimpleSpanProcessor, BatchSpanProcessor)):
109
+ processor_cls = processor.__class__.__name__
110
+ if isinstance(processor.span_exporter, LightningStoreOTLPExporter):
111
+ # This should be the main path now.
112
+ processors.append(f"{active_span_processor_cls} - {processor_cls} - {processor.span_exporter!r}")
113
+ elif _is_agentops_exporter(processor.span_exporter):
114
+ # You need to be careful if the code goes into this path.
115
+ endpoint = processor.span_exporter._endpoint # pyright: ignore[reportPrivateUsage]
116
+ processors.append(
117
+ f"{active_span_processor_cls} - {processor_cls} - "
118
+ f"{processor.span_exporter.__class__.__name__}(endpoint={endpoint!r})"
119
+ )
120
+ else:
121
+ # Other cases like Console Span Exporter.
122
+ processors.append(
123
+ f"{active_span_processor_cls} - {processor_cls} - {processor.span_exporter.__class__.__name__}"
124
+ )
125
+ else:
126
+ processors.append(f"{active_span_processor_cls} - {processor.__class__.__name__}")
127
+
128
+ logger.debug(f"Tracer provider: {tracer_provider!r}. Active span processors:")
129
+ for processor in processors:
130
+ logger.debug(" * " + processor)
131
+
132
+ return tracer_provider
133
+
134
+
135
+ def get_span_processors(
136
+ tracer_provider: TracerProviderImpl, expected_type: Type[T_SpanProcessor]
137
+ ) -> List[T_SpanProcessor]:
138
+ """Get the span processors from the tracer provider.
139
+
140
+ Args:
141
+ tracer_provider: The tracer provider to get the span processors from.
142
+ expected_type: The type of the span processors to get.
143
+
144
+ Returns:
145
+ A list of span processors of the expected type.
146
+ """
147
+ processors: List[T_SpanProcessor] = []
148
+ for processor in tracer_provider._active_span_processor._span_processors: # pyright: ignore[reportPrivateUsage]
149
+ if isinstance(processor, expected_type):
150
+ processors.append(processor)
151
+ return processors
152
+
153
+
154
+ def get_tracer(use_active_span_processor: bool = True) -> trace_api.Tracer:
155
+ """Resolve the OpenTelemetry tracer configured for GatewaySDK.
156
+
157
+ Args:
158
+ use_active_span_processor: Whether to use the active span processor.
159
+
160
+ Returns:
161
+ OpenTelemetry tracer tagged with the `gatewaysdk` instrumentation name.
162
+
163
+ Raises:
164
+ RuntimeError: If OpenTelemetry was not initialized before calling this helper.
165
+ """
166
+ if hasattr(trace_api, "_TRACER_PROVIDER") and trace_api._TRACER_PROVIDER is None: # type: ignore[attr-defined]
167
+ raise RuntimeError("Tracer is not initialized. Cannot emit a meaningful span.")
168
+
169
+ tracer_provider = get_tracer_provider(inspect=True) # inspection is on by default
170
+
171
+ if use_active_span_processor:
172
+ return tracer_provider.get_tracer("gatewaysdk")
173
+
174
+ else:
175
+ filterwarnings(
176
+ "ignore",
177
+ message=r"You should use InstrumentationScope. Deprecated since version 1.11.1.",
178
+ category=DeprecationWarning,
179
+ module="opentelemetry.sdk.trace",
180
+ )
181
+
182
+ return Tracer(
183
+ tracer_provider.sampler,
184
+ tracer_provider.resource,
185
+ # We use an empty span processor to avoid emitting spans to the tracer
186
+ SynchronousMultiSpanProcessor(),
187
+ tracer_provider.id_generator,
188
+ InstrumentationInfo("gatewaysdk", "", ""), # type: ignore
189
+ SpanLimits(),
190
+ InstrumentationScope(
191
+ "gatewaysdk",
192
+ "",
193
+ "",
194
+ {},
195
+ ),
196
+ )
197
+
198
+
199
+ def make_tag_attributes(tags: List[str]) -> Dict[str, Any]:
200
+ """Convert a list of tags into flattened attributes for span tagging.
201
+
202
+ There is no syntax enforced for tags, they are just strings. For example:
203
+
204
+ ```python
205
+ ["gen_ai.model:gpt-4", "reward.extrinsic"]
206
+ ```
207
+ """
208
+ return flatten_attributes({LightningSpanAttributes.TAG.value: tags}, expand_leaf_lists=True)
209
+
210
+
211
+ def extract_tags_from_attributes(attributes: Dict[str, Any]) -> List[str]:
212
+ """Extract tag attributes from flattened span attributes.
213
+
214
+ Args:
215
+ attributes: A dictionary of flattened span attributes.
216
+ """
217
+ maybe_tag_list = filter_and_unflatten_attributes(attributes, LightningSpanAttributes.TAG.value)
218
+ return TypeAdapter(List[str]).validate_python(maybe_tag_list)
219
+
220
+
221
+ def make_link_attributes(links: Dict[str, str]) -> Dict[str, Any]:
222
+ """Convert a dictionary of links into flattened attributes for span linking.
223
+
224
+ Links example:
225
+
226
+ ```python
227
+ {
228
+ "gen_ai.response.id": "response-123",
229
+ "span_id": "abcd-efgh-ijkl",
230
+ }
231
+ ```
232
+ """
233
+ link_list: List[Dict[str, str]] = []
234
+ for key, value in links.items():
235
+ if not isinstance(value, str): # pyright: ignore[reportUnnecessaryIsInstance]
236
+ raise ValueError(f"Link value must be a string, got {type(value)} for key '{key}'")
237
+ link_list.append({LinkAttributes.KEY_MATCH.value: key, LinkAttributes.VALUE_MATCH.value: value})
238
+ return flatten_attributes({LightningSpanAttributes.LINK.value: link_list}, expand_leaf_lists=True)
239
+
240
+
241
+ def query_linked_spans(spans: Sequence[T_SpanLike], links: List[LinkPydanticModel]) -> List[T_SpanLike]:
242
+ """Query spans that are linked by the given link attributes.
243
+
244
+ Args:
245
+ spans: A sequence of spans to search.
246
+ links: A list of link attributes to match.
247
+
248
+ Returns:
249
+ A list of spans that match the given link attributes.
250
+ """
251
+ matched_spans: List[T_SpanLike] = []
252
+
253
+ for span in spans:
254
+ span_attributes = span.attributes or {}
255
+ is_match = True
256
+ for link in links:
257
+ # trace_id and span_id must be full match.
258
+ if link.key_match == "trace_id":
259
+ if isinstance(span, ReadableSpan):
260
+ trace_id = trace_api.format_trace_id(span.context.trace_id) if span.context else None
261
+ else:
262
+ trace_id = span.trace_id
263
+ if trace_id != link.value_match:
264
+ is_match = False
265
+ break
266
+
267
+ elif link.key_match == "span_id":
268
+ if isinstance(span, ReadableSpan):
269
+ span_id = trace_api.format_span_id(span.context.span_id) if span.context else None
270
+ else:
271
+ span_id = span.span_id
272
+ if span_id != link.value_match:
273
+ is_match = False
274
+ break
275
+
276
+ else:
277
+ attribute = span_attributes.get(link.key_match)
278
+ # attributes must also be a full match currently.
279
+ if attribute != link.value_match:
280
+ is_match = False
281
+ break
282
+
283
+ if is_match:
284
+ matched_spans.append(span)
285
+
286
+ return matched_spans
287
+
288
+
289
+ def extract_links_from_attributes(attributes: Dict[str, Any]) -> List[LinkPydanticModel]:
290
+ """Extract link attributes from flattened span attributes.
291
+
292
+ Args:
293
+ attributes: A dictionary of flattened span attributes.
294
+ """
295
+ maybe_link_list = filter_and_unflatten_attributes(attributes, LightningSpanAttributes.LINK.value)
296
+ return TypeAdapter(List[LinkPydanticModel]).validate_python(maybe_link_list)
297
+
298
+
299
+ def filter_attributes(attributes: Dict[str, Any], prefix: str) -> Dict[str, Any]:
300
+ """Filter attributes that start with the given prefix.
301
+
302
+ The attribute must start with `prefix.` or be exactly `prefix` to be included.
303
+
304
+ Args:
305
+ attributes: A dictionary of span attributes.
306
+ prefix: The prefix to filter by.
307
+
308
+ Returns:
309
+ A dictionary of attributes that start with the given prefix.
310
+ """
311
+ return {k: v for k, v in attributes.items() if k.startswith(prefix + ".") or k == prefix}
312
+
313
+
314
+ def filter_and_unflatten_attributes(attributes: Dict[str, Any], prefix: str) -> Union[Dict[str, Any], List[Any]]:
315
+ """Filter attributes that start with the given prefix and unflatten them.
316
+ The prefix will be removed during unflattening.
317
+
318
+ Args:
319
+ attributes: A dictionary of span attributes.
320
+ prefix: The prefix to filter by.
321
+
322
+ Returns:
323
+ A nested dictionary or list of attributes that start with the given prefix.
324
+ """
325
+ filtered_attributes = filter_attributes(attributes, prefix)
326
+ stripped_attributes: Dict[str, Any] = {}
327
+ for k, v in filtered_attributes.items():
328
+ if k == prefix:
329
+ raise ValueError(f"Cannot unflatten attribute with key exactly equal to prefix: {prefix}")
330
+ else:
331
+ stripped_key = k[len(prefix) + 1 :] # +1 to remove the dot
332
+ stripped_attributes[stripped_key] = v
333
+ return unflatten_attributes(stripped_attributes)
334
+
335
+
336
+ def flatten_attributes(
337
+ nested_data: Union[Dict[str, Any], List[Any]], *, expand_leaf_lists: bool = False
338
+ ) -> Dict[str, Any]:
339
+ """Flatten a nested dictionary or list into a flat dictionary with dotted keys.
340
+
341
+ This function recursively traverses dictionaries and lists, producing a flat
342
+ key-value mapping where nested paths are represented via dot-separated keys.
343
+ Lists are indexed numerically.
344
+
345
+ Example:
346
+
347
+ >>> flatten_attributes({"a": {"b": 1, "c": [2, 3]}}, expand_leaf_lists=True)
348
+ {"a.b": 1, "a.c.0": 2, "a.c.1": 3}
349
+
350
+ Args:
351
+ nested_data: A nested structure composed of dictionaries, lists, or primitive values.
352
+ expand_leaf_lists: Whether to expand lists composed only of primitive values.
353
+ When `False` (the default), lists of str/int/float/bool are treated as
354
+ leaf values and stored without enumerating their indices.
355
+
356
+ Returns:
357
+ A flat dictionary mapping dotted-string paths to primitive values.
358
+ """
359
+
360
+ flat: Dict[str, Any] = {}
361
+
362
+ def _primitive_type(value: Any) -> Union[type[str], type[int], type[float], type[bool]]:
363
+ if isinstance(value, bool):
364
+ return bool
365
+ if isinstance(value, int):
366
+ return int
367
+ if isinstance(value, float):
368
+ return float
369
+ return str
370
+
371
+ def _walk(value: Any, prefix: str = "") -> None:
372
+ if isinstance(value, dict):
373
+ for k, v in cast(Dict[Any, Any], value).items():
374
+ if not isinstance(k, str):
375
+ raise ValueError(
376
+ f"Only string keys are supported in dictionaries, got '{k}' of type {type(k)} in {prefix}"
377
+ )
378
+ new_prefix = f"{prefix}.{k}" if prefix else k
379
+ _walk(v, new_prefix)
380
+ elif isinstance(value, list):
381
+ maybe_list = cast(List[Any], value)
382
+ is_leaf_candidate = bool(maybe_list) and all(
383
+ isinstance(item, (str, int, float, bool)) for item in maybe_list
384
+ )
385
+ if not expand_leaf_lists and is_leaf_candidate and prefix:
386
+ primitive_types = {_primitive_type(item) for item in maybe_list}
387
+ if len(primitive_types) == 1:
388
+ flat[prefix] = maybe_list
389
+ return
390
+ logger.warning(
391
+ "List attribute '%s' contains mixed primitive types %s; expanding indexed keys instead.",
392
+ prefix,
393
+ primitive_types,
394
+ )
395
+
396
+ for idx, item in enumerate(maybe_list):
397
+ new_prefix = f"{prefix}.{idx}" if prefix else str(idx)
398
+ _walk(item, new_prefix)
399
+ else:
400
+ flat[prefix] = value
401
+
402
+ _walk(nested_data)
403
+ return flat
404
+
405
+
406
+ def unflatten_attributes(flat_data: Dict[str, Any]) -> Union[Dict[str, Any], List[Any]]:
407
+ """Reconstruct a nested dictionary/list structure from a flat dictionary.
408
+
409
+ Keys are dot-separated paths. Segments that are digit strings will only
410
+ become list indices if *all* keys in that dict form a consecutive
411
+ 0..n-1 range. Otherwise they remain dict keys.
412
+
413
+ Example:
414
+
415
+ >>> unflatten_attributes({"a.b": 1, "a.c.0": 2, "a.c.1": 3})
416
+ {"a": {"b": 1, "c": [2, 3]}}
417
+
418
+ Args:
419
+ flat_data: A dictionary whose keys are dot-separated paths and whose
420
+ values are primitive data elements.
421
+
422
+ Returns:
423
+ A nested dictionary (and lists where appropriate) corresponding to
424
+ the flattened structure.
425
+ """
426
+ # 1) Build a pure dict tree first (no lists yet)
427
+ root: Dict[str, Any] = {}
428
+
429
+ for flat_key, value in flat_data.items():
430
+ parts = flat_key.split(".")
431
+ curr: Dict[str, Any] = root
432
+
433
+ for part in parts[:-1]:
434
+ # Ensure intermediate node is a dict
435
+ if part not in curr or not isinstance(curr[part], dict):
436
+ curr[part] = {}
437
+ curr = curr[part] # type: ignore[assignment]
438
+
439
+ curr[parts[-1]] = value
440
+
441
+ # 2) Recursively convert dicts-with-consecutive-numeric-keys into lists
442
+ def convert(node: Union[Dict[str, Any], List[Any]]) -> Union[Dict[str, Any], List[Any]]:
443
+ if isinstance(node, dict):
444
+ # First convert children
445
+ for k, v in list(node.items()):
446
+ node[k] = convert(v)
447
+
448
+ if not node:
449
+ # empty dict stays dict
450
+ return node
451
+
452
+ # Check if keys are all numeric strings
453
+ keys = list(node.keys())
454
+ if all(isinstance(k, str) and k.isdigit() for k in keys): # pyright: ignore[reportUnnecessaryIsInstance]
455
+ indices = sorted(int(k) for k in keys)
456
+ # Must be exactly 0..n-1
457
+ if indices == list(range(len(indices))):
458
+ return [node[str(i)] for i in range(len(indices))]
459
+
460
+ return node
461
+
462
+ if isinstance(node, list): # pyright: ignore[reportUnnecessaryIsInstance]
463
+ return [convert(v) for v in node]
464
+
465
+ # Keep as is
466
+ return node
467
+
468
+ return convert(root)
469
+
470
+
471
+ def sanitize_attribute_value(object: Any, force: bool = True) -> AttributeValue:
472
+ """Sanitize an attribute value to be a valid OpenTelemetry attribute value."""
473
+ if isinstance(object, (str, int, float, bool)):
474
+ return object
475
+
476
+ if isinstance(object, list):
477
+ try:
478
+ return sanitize_list_attribute_sanity(cast(List[Any], object))
479
+ except ValueError as exc:
480
+ logger.warning(f"Failed to sanitize list attribute. Fallback to JSON serialization: {exc}")
481
+
482
+ try:
483
+ # This include null, dict, etc.
484
+ serialized = json.dumps(object, default=str if force else None)
485
+ except (TypeError, ValueError) as exc:
486
+ raise ValueError(f"Object must be JSON serializable, got: {type(cast(Any, object))}.") from exc
487
+ return serialized
488
+
489
+
490
+ def sanitize_attributes(attributes: Dict[str, Any], force: bool = True) -> Attributes:
491
+ """Sanitize a dictionary of attributes to be a valid OpenTelemetry attributes.
492
+
493
+ Args:
494
+ attributes: A dictionary of attributes to sanitize.
495
+ force: Whether to force sanitization even when the value is not JSON serializable.
496
+ """
497
+ result: Attributes = {}
498
+ for k, v in attributes.items():
499
+ try:
500
+ result[k] = sanitize_attribute_value(v, force=force)
501
+ except ValueError as exc:
502
+ raise ValueError(f"Failed to sanitize attribute '{k}': {exc}") from exc
503
+ return result
504
+
505
+
506
+ def sanitize_list_attribute_sanity(maybe_list: List[Any]) -> AttributeValue:
507
+ """Try to sanitize a list of attributes to be a valid OpenTelemetry attribute value.
508
+
509
+ Raise error if the list contains multiple types of primitive values.
510
+ """
511
+ if all(isinstance(item, str) for item in maybe_list):
512
+ return list[str](maybe_list)
513
+ if all(isinstance(item, bool) for item in maybe_list):
514
+ return list[bool](maybe_list)
515
+ if all(isinstance(item, (int, bool)) for item in maybe_list):
516
+ return [int(item) for item in maybe_list]
517
+ if all(isinstance(item, (float, int, bool)) for item in maybe_list):
518
+ return [float(item) for item in maybe_list]
519
+
520
+ list_types: List[Any] = [type(item) for item in maybe_list]
521
+ raise ValueError(f"List must contain only one type of primitive values, got: {set(list_types)}.")
522
+
523
+
524
+ def check_attributes_sanity(attributes: Dict[Any, Any]) -> None:
525
+ """Check if a dictionary of attributes is a valid OpenTelemetry attributes."""
526
+ for k, v in attributes.items():
527
+ if not isinstance(k, str):
528
+ raise ValueError(f"Attribute key must be a string, got {type(k)} for key '{k}'")
529
+ if isinstance(v, list):
530
+ try:
531
+ sanitize_list_attribute_sanity(cast(List[Any], v))
532
+ except ValueError as exc:
533
+ raise ValueError(f"Failed to sanitize list attribute '{k}': {exc}") from exc
534
+ elif not isinstance(v, (str, int, float, bool)):
535
+ raise ValueError(
536
+ f"Attribute value must be a string, int, float, bool, or list of these, got {type(v)} for value '{v}'"
537
+ )
538
+
539
+
540
+ def format_exception_attributes(exception: BaseException) -> Attributes:
541
+ """Format an exception into a dictionary of attributes."""
542
+ stacktrace = "".join(traceback.format_exception(type(exception), exception, exception.__traceback__))
543
+ span_attributes: Attributes = {
544
+ exception_attributes.EXCEPTION_TYPE: type(exception).__name__,
545
+ exception_attributes.EXCEPTION_MESSAGE: str(exception),
546
+ exception_attributes.EXCEPTION_ESCAPED: True,
547
+ }
548
+ if stacktrace.strip():
549
+ span_attributes[exception_attributes.EXCEPTION_STACKTRACE] = stacktrace
550
+ return span_attributes