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,294 @@
1
+ # Instrumentation Principles for GatewaySDK
2
+
3
+ Lessons learned from building instrumentors for agent SDKs like `claude-agent-sdk`.
4
+
5
+ ## 1. Know Your Backend's Attribute Expectations
6
+
7
+ Different tracing backends extract data from different attribute namespaces.
8
+
9
+ **Example:** Gateway's attribute resolution checks multiple namespaces in priority order
10
+ (see `attributeAliases.ts`). The primary namespace is `gateway.observation.*`:
11
+
12
+ ```typescript
13
+ // Priority order for observation.input (from attributeAliases.ts):
14
+ // 1. gateway.observation.input ← primary (GatewaySDK uses this)
15
+ // 2. langfuse.observation.input ← compatibility
16
+ // 3. input.value ← OpenInference
17
+ // 4. gen_ai.input.messages ← OTel GenAI
18
+ // ... (20+ other aliases)
19
+ ```
20
+
21
+ **Solution:** Set multiple attribute conventions for maximum compatibility:
22
+
23
+ ```python
24
+ # Langfuse (for Gateway visualization)
25
+ span.set_attribute("langfuse.observation.input", input_value)
26
+ span.set_attribute("langfuse.observation.output", output_value)
27
+
28
+ # OpenInference (for Phoenix, Arize)
29
+ span.set_attribute("input.value", input_value)
30
+ span.set_attribute("output.value", output_value)
31
+ span.set_attribute("openinference.span.kind", "LLM")
32
+
33
+ # GenAI (widely supported standard)
34
+ span.set_attribute("gen_ai.system", "anthropic")
35
+ span.set_attribute("gen_ai.usage.input_tokens", token_count)
36
+ ```
37
+
38
+ ## 2. Context Propagation is Critical
39
+
40
+ For child spans to appear correctly nested and for timing to be accurate, you must **activate** the parent context.
41
+
42
+ **Bad:**
43
+ ```python
44
+ parent_span = tracer.start_span("parent")
45
+ # Other instrumentation won't see this as the active parent
46
+ child_span = tracer.start_span("child") # Will be a sibling, not child!
47
+ ```
48
+
49
+ **Good:**
50
+ ```python
51
+ parent_span = tracer.start_span("parent")
52
+ parent_ctx = trace.set_span_in_context(parent_span)
53
+ context_token = attach(parent_ctx) # Activate!
54
+
55
+ try:
56
+ # Now other instrumentation sees parent_span as active
57
+ # Their spans will be children automatically
58
+ result = await some_instrumented_function()
59
+ finally:
60
+ detach(context_token) # Clean up
61
+ parent_span.end()
62
+ ```
63
+
64
+ **Even Better (explicit context for child spans):**
65
+ ```python
66
+ parent_ctx = trace.set_span_in_context(parent_span)
67
+ context_token = attach(parent_ctx)
68
+
69
+ # Explicitly pass context when creating known children
70
+ child_span = tracer.start_span("child", context=parent_ctx)
71
+ ```
72
+
73
+ ## 3. Root Spans Need Fresh Context
74
+
75
+ To create separate traces (not nested), use an empty `Context()`:
76
+
77
+ ```python
78
+ # Creates a NEW trace (root span)
79
+ span = tracer.start_span("operation", context=Context())
80
+
81
+ # Without context=Context(), this would be a child of any active span
82
+ ```
83
+
84
+ **Use case:** In chat applications, each user message should be its own trace, not nested under previous messages.
85
+
86
+ ## 4. Async Generators Require Special Handling
87
+
88
+ Spans must remain open across async generator boundaries.
89
+
90
+ **Pattern:**
91
+ ```python
92
+ def _create_instrumented_query(self, original_query):
93
+ async def instrumented_query(client, prompt):
94
+ span = tracer.start_span("conversation")
95
+ span.set_attribute("input", prompt)
96
+
97
+ # Store span for later use
98
+ setattr(client, "_current_span", span)
99
+
100
+ try:
101
+ return await original_query(client, prompt)
102
+ except Exception as e:
103
+ span.set_status(StatusCode.ERROR)
104
+ span.end() # End early on error
105
+ delattr(client, "_current_span")
106
+ raise
107
+
108
+ def _create_instrumented_receive(self, original_receive):
109
+ async def instrumented_receive(client):
110
+ span = getattr(client, "_current_span", None)
111
+
112
+ try:
113
+ async for item in original_receive(client):
114
+ # Process items, set output on span
115
+ yield item
116
+
117
+ if is_final_item(item):
118
+ span.set_attribute("output", extract_output(item))
119
+ span.end()
120
+ delattr(client, "_current_span")
121
+ finally:
122
+ # Cleanup if generator not fully consumed
123
+ if hasattr(client, "_current_span"):
124
+ remaining = getattr(client, "_current_span")
125
+ if remaining.is_recording():
126
+ remaining.end()
127
+ delattr(client, "_current_span")
128
+ ```
129
+
130
+ ## 5. Capture All Available Context
131
+
132
+ Don't just capture the obvious fields - explore the SDK's type definitions for all available data.
133
+
134
+ **Example:** `claude-agent-sdk` provides:
135
+ ```python
136
+ AssistantMessage.error # API error type (rate_limit, billing_error, etc.)
137
+ AssistantMessage.parent_tool_use_id # Sub-agent correlation
138
+ ThinkingBlock.signature # Thinking block signature
139
+ ResultMessage.structured_output # Structured response data
140
+ SystemMessage.subtype # System event type (init, mcp_connection, etc.)
141
+ ```
142
+
143
+ Capture them all for maximum observability:
144
+
145
+ ```python
146
+ if message.error:
147
+ span.set_attribute("claude.api_error", message.error)
148
+
149
+ if isinstance(message, SystemMessage):
150
+ span.add_event(
151
+ f"system.{message.subtype}",
152
+ attributes={"system.data": json.dumps(message.data)}
153
+ )
154
+ ```
155
+
156
+ ## 6. Never Truncate Arbitrarily
157
+
158
+ Truncating attributes like `[:500]` or `[:10000]` breaks observability.
159
+
160
+ **Bad:**
161
+ ```python
162
+ span.set_attribute("output", long_text[:500]) # Why 500?
163
+ ```
164
+
165
+ **Good:**
166
+ ```python
167
+ span.set_attribute("output", long_text) # Let OTEL handle limits
168
+ ```
169
+
170
+ OpenTelemetry SDKs have configurable limits. Arbitrary truncation in instrumentors:
171
+ - Loses critical debugging data
172
+ - Isn't configurable by users
173
+ - Often removes the exact data needed for diagnosis
174
+
175
+ ## 7. Span Kinds: OTel vs. Domain
176
+
177
+ OpenTelemetry has `SpanKind` (CLIENT, SERVER, INTERNAL), but domain conventions like OpenInference use attributes.
178
+
179
+ **Set both:**
180
+ ```python
181
+ # OTel SpanKind (for protocol semantics)
182
+ span = tracer.start_span("llm_call", kind=SpanKind.CLIENT)
183
+
184
+ # Domain span kind (for tracing UI semantics)
185
+ span.set_attribute("openinference.span.kind", "LLM")
186
+ ```
187
+
188
+ Common domain kinds:
189
+ - `LLM` - Language model generation
190
+ - `TOOL` - Tool/function execution
191
+ - `AGENT` - Agent orchestration
192
+ - `CHAIN` - Workflow steps
193
+
194
+ ## 8. Timing Accuracy Limitations
195
+
196
+ Be aware that span timing in streaming contexts reflects **when you receive messages**, not when work actually happened.
197
+
198
+ ```python
199
+ # In claude-agent-sdk:
200
+ ToolUseBlock arrives → start tool span (time T1)
201
+ # ... tool executes in CLI subprocess (actual work happens)
202
+ ToolResultBlock arrives → end tool span (time T2)
203
+
204
+ # T2 - T1 includes:
205
+ # - Actual tool execution time
206
+ # - Stream buffering delay
207
+ # - Network latency
208
+ # - Message parsing overhead
209
+ ```
210
+
211
+ **Document this limitation:**
212
+ ```python
213
+ # Note: Span timing is based on when we receive ToolUseBlock/ToolResultBlock messages,
214
+ # not when the tool actually executes in Claude CLI. This may cause timing inaccuracies.
215
+ tool_span = tracer.start_span(f"tool.{block.name}", ...)
216
+ ```
217
+
218
+ **Alternative (if SDK provides timing):**
219
+ ```python
220
+ # Some SDKs provide explicit timestamps in result messages
221
+ if hasattr(message, 'started_at') and hasattr(message, 'completed_at'):
222
+ tool_span = tracer.start_span(
223
+ name=f"tool.{name}",
224
+ start_time=parse_timestamp(message.started_at), # Explicit start
225
+ )
226
+ tool_span.end(end_time=parse_timestamp(message.completed_at)) # Explicit end
227
+ ```
228
+
229
+ ## 9. Centralize Attribute Definitions
230
+
231
+ Don't hardcode attribute names throughout your instrumentor.
232
+
233
+ **Bad:**
234
+ ```python
235
+ span.set_attribute("langfuse.observation.input", value)
236
+ # ...later in another file...
237
+ span.set_attribute("langfuse.observation.input", value) # Typo risk!
238
+ ```
239
+
240
+ **Good:**
241
+ ```python
242
+ # semconv.py
243
+ LANGFUSE_OBSERVATION_INPUT = "langfuse.observation.input"
244
+ LANGFUSE_OBSERVATION_OUTPUT = "langfuse.observation.output"
245
+
246
+ # instrumentor.py
247
+ from ..semconv import LANGFUSE_OBSERVATION_INPUT
248
+ span.set_attribute(LANGFUSE_OBSERVATION_INPUT, value)
249
+ ```
250
+
251
+ Benefits:
252
+ - Type safety
253
+ - Easier refactoring
254
+ - Single source of truth
255
+ - Documentation via constants
256
+
257
+ ## 10. Preserve Event Sequence
258
+
259
+ When LLMs interleave text and tool calls, preserve the order:
260
+
261
+ **Bad (loses sequence):**
262
+ ```python
263
+ # Concatenate all text, list all tools
264
+ output = {
265
+ "text": text1 + text2,
266
+ "tool_calls": [tool1, tool2]
267
+ }
268
+ ```
269
+
270
+ **Good (preserves sequence):**
271
+ ```python
272
+ # Preserve: text → tool_call → text
273
+ output_blocks = [
274
+ {"type": "text", "content": text1},
275
+ {"type": "tool_call", "name": "search", "input": {...}, "output": "..."},
276
+ {"type": "text", "content": text2},
277
+ ]
278
+ span.set_attribute("output", json.dumps(output_blocks))
279
+ ```
280
+
281
+ This helps understand the agent's reasoning flow: "It said X, then called tool Y, then concluded Z."
282
+
283
+ ## Summary
284
+
285
+ Building robust instrumentors requires:
286
+ 1. ✅ Understanding backend attribute expectations
287
+ 2. ✅ Proper context propagation with `attach()`/`detach()`
288
+ 3. ✅ Supporting multiple attribute conventions
289
+ 4. ✅ Careful span lifecycle management in async code
290
+ 5. ✅ Capturing all available SDK data
291
+ 6. ✅ Avoiding arbitrary truncation
292
+ 7. ✅ Being aware of timing limitations in streaming contexts
293
+ 8. ✅ Centralizing attribute definitions
294
+ 9. ✅ Preserving event sequences for debugging
@@ -0,0 +1,352 @@
1
+ # Copyright (c) Gateway. All rights reserved.
2
+
3
+ """Instrumentor registry for GatewayDK tracing.
4
+
5
+ This module provides a registry-based system for discovering and managing
6
+ OpenTelemetry instrumentors. It supports lazy loading and optional dependencies.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import importlib.util
12
+ import logging
13
+ from abc import ABC, abstractmethod
14
+ from typing import TYPE_CHECKING, Dict, List, Optional
15
+
16
+ if TYPE_CHECKING:
17
+ from openinference.instrumentation import TraceConfig
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Singleton registry instance
22
+ _registry: Optional["InstrumentorRegistry"] = None
23
+
24
+
25
+ class BaseInstrumentor(ABC):
26
+ """Base class for instrumentor adapters.
27
+
28
+ Each instrumentor adapter wraps a specific instrumentation library
29
+ (OpenInference, AgentOps, etc.) and provides a consistent interface.
30
+ """
31
+
32
+ @property
33
+ @abstractmethod
34
+ def name(self) -> str:
35
+ """Unique name for this instrumentor (e.g., 'openai', 'langchain')."""
36
+ pass
37
+
38
+ @property
39
+ @abstractmethod
40
+ def package_name(self) -> str:
41
+ """The package name to check for availability."""
42
+ pass
43
+
44
+ @abstractmethod
45
+ def is_available(self) -> bool:
46
+ """Check if the instrumentor and its target library are available."""
47
+ pass
48
+
49
+ @abstractmethod
50
+ def instrument(self, trace_config: Optional["TraceConfig"] = None) -> None:
51
+ """Activate the instrumentation."""
52
+ pass
53
+
54
+ @abstractmethod
55
+ def uninstrument(self) -> None:
56
+ """Deactivate the instrumentation."""
57
+ pass
58
+
59
+
60
+ class OpenInferenceInstrumentor(BaseInstrumentor):
61
+ """Adapter for OpenInference instrumentors.
62
+
63
+ OpenInference provides high-quality instrumentors for major LLM libraries
64
+ that emit semantic conventions compatible with observability platforms.
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ name: str,
70
+ instrumentor_package: str,
71
+ instrumentor_class: str,
72
+ target_package: str,
73
+ ):
74
+ """Initialize the adapter.
75
+
76
+ Args:
77
+ name: Unique name for this instrumentor.
78
+ instrumentor_package: Full package path to the instrumentor module.
79
+ instrumentor_class: Class name of the instrumentor.
80
+ target_package: The target library package (e.g., "openai").
81
+ """
82
+ self._name = name
83
+ self._instrumentor_package = instrumentor_package
84
+ self._instrumentor_class = instrumentor_class
85
+ self._target_package = target_package
86
+ self._instance: Optional[object] = None
87
+ self._instrumented = False
88
+
89
+ @property
90
+ def name(self) -> str:
91
+ return self._name
92
+
93
+ @property
94
+ def package_name(self) -> str:
95
+ return self._instrumentor_package
96
+
97
+ @property
98
+ def target_package(self) -> str:
99
+ """The AI library this instrumentor traces (e.g. "openai")."""
100
+ return self._target_package
101
+
102
+ def is_available(self) -> bool:
103
+ """Check if both the instrumentor and target library are installed."""
104
+ # Check target library
105
+ if not _is_package_available(self._target_package):
106
+ return False
107
+
108
+ # Check instrumentor package
109
+ if not _is_package_available(self._instrumentor_package):
110
+ return False
111
+
112
+ return True
113
+
114
+ def instrument(self, trace_config: Optional["TraceConfig"] = None) -> None:
115
+ """Activate the OpenInference instrumentation."""
116
+ if self._instrumented:
117
+ logger.debug("Already instrumented: %s", self._name)
118
+ return
119
+
120
+ if not self.is_available():
121
+ logger.debug("Cannot instrument %s: dependencies not available", self._name)
122
+ return
123
+
124
+ try:
125
+ module = importlib.import_module(self._instrumentor_package)
126
+ instrumentor_class = getattr(module, self._instrumentor_class)
127
+ self._instance = instrumentor_class()
128
+
129
+ # OpenInference instrumentors accept trace_config
130
+ if trace_config is not None:
131
+ self._instance.instrument(tracer_provider=None, trace_config=trace_config)
132
+ else:
133
+ self._instance.instrument()
134
+
135
+ self._instrumented = True
136
+ logger.debug("Instrumented: %s", self._name)
137
+ except Exception as e:
138
+ logger.warning("Failed to instrument %s: %s", self._name, e)
139
+ raise
140
+
141
+ def uninstrument(self) -> None:
142
+ """Deactivate the OpenInference instrumentation."""
143
+ if not self._instrumented or self._instance is None:
144
+ return
145
+
146
+ try:
147
+ self._instance.uninstrument()
148
+ self._instrumented = False
149
+ self._instance = None
150
+ logger.debug("Uninstrumented: %s", self._name)
151
+ except Exception as e:
152
+ logger.warning("Failed to uninstrument %s: %s", self._name, e)
153
+
154
+
155
+ class InstrumentorRegistry:
156
+ """Registry for managing instrumentors.
157
+
158
+ The registry provides a central place to discover, configure, and manage
159
+ instrumentors from various sources.
160
+ """
161
+
162
+ def __init__(self):
163
+ self._instrumentors: Dict[str, BaseInstrumentor] = {}
164
+ self._register_defaults()
165
+
166
+ def _register_defaults(self) -> None:
167
+ """Register the default set of instrumentors."""
168
+ # Claude Agent SDK instrumentor (native gatewaysdk instrumentor)
169
+ from .claude_agent_sdk import ClaudeAgentSDKInstrumentor
170
+
171
+ self.register(ClaudeAgentSDKInstrumentor())
172
+
173
+ # OpenInference instrumentors (core set)
174
+ openinference_instrumentors = [
175
+ ("openai_agents", "openinference.instrumentation.openai_agents", "OpenAIAgentsInstrumentor", "agents"),
176
+ ("openai", "openinference.instrumentation.openai", "OpenAIInstrumentor", "openai"),
177
+ ("anthropic", "openinference.instrumentation.anthropic", "AnthropicInstrumentor", "anthropic"),
178
+ ("langchain", "openinference.instrumentation.langchain", "LangChainInstrumentor", "langchain"),
179
+ ("llama_index", "openinference.instrumentation.llama_index", "LlamaIndexInstrumentor", "llama_index"),
180
+ ("litellm", "openinference.instrumentation.litellm", "LiteLLMInstrumentor", "litellm"),
181
+ # Additional instrumentors
182
+ ("google_adk", "openinference.instrumentation.google_adk", "GoogleADKInstrumentor", "google.adk"),
183
+ ("mistral", "openinference.instrumentation.mistralai", "MistralAIInstrumentor", "mistralai"),
184
+ ("groq", "openinference.instrumentation.groq", "GroqInstrumentor", "groq"),
185
+ ("bedrock", "openinference.instrumentation.bedrock", "BedrockInstrumentor", "boto3"),
186
+ ("vertexai", "openinference.instrumentation.vertexai", "VertexAIInstrumentor", "vertexai"),
187
+ ("dspy", "openinference.instrumentation.dspy", "DSPyInstrumentor", "dspy"),
188
+ ("instructor", "openinference.instrumentation.instructor", "InstructorInstrumentor", "instructor"),
189
+ ("crewai", "openinference.instrumentation.crewai", "CrewAIInstrumentor", "crewai"),
190
+ ]
191
+
192
+ for name, package, class_name, target in openinference_instrumentors:
193
+ instrumentor = OpenInferenceInstrumentor(
194
+ name=name,
195
+ instrumentor_package=package,
196
+ instrumentor_class=class_name,
197
+ target_package=target,
198
+ )
199
+ self.register(instrumentor)
200
+
201
+ def register(self, instrumentor: BaseInstrumentor) -> None:
202
+ """Register an instrumentor.
203
+
204
+ Args:
205
+ instrumentor: The instrumentor to register.
206
+ """
207
+ self._instrumentors[instrumentor.name] = instrumentor
208
+ logger.debug("Registered instrumentor: %s", instrumentor.name)
209
+
210
+ def get(self, name: str) -> Optional[BaseInstrumentor]:
211
+ """Get an instrumentor by name.
212
+
213
+ Args:
214
+ name: The instrumentor name.
215
+
216
+ Returns:
217
+ The instrumentor if found and available, None otherwise.
218
+ """
219
+ instrumentor = self._instrumentors.get(name)
220
+ if instrumentor is None:
221
+ return None
222
+
223
+ if not instrumentor.is_available():
224
+ return None
225
+
226
+ return instrumentor
227
+
228
+ def list_available(self) -> List[str]:
229
+ """List all available instrumentor names.
230
+
231
+ Returns:
232
+ List of instrumentor names that are currently available.
233
+ """
234
+ return [name for name, instrumentor in self._instrumentors.items() if instrumentor.is_available()]
235
+
236
+ def list_all(self) -> List[str]:
237
+ """List all registered instrumentor names.
238
+
239
+ Returns:
240
+ List of all registered instrumentor names (including unavailable).
241
+ """
242
+ return list(self._instrumentors.keys())
243
+
244
+ def instrument_all(
245
+ self,
246
+ names: Optional[List[str]] = None,
247
+ skip: Optional[List[str]] = None,
248
+ trace_config: Optional["TraceConfig"] = None,
249
+ ) -> List[str]:
250
+ """Instrument multiple instrumentors at once.
251
+
252
+ Args:
253
+ names: List of instrumentor names to enable. If None, uses the core set.
254
+ skip: List of instrumentor names to skip.
255
+ trace_config: Optional TraceConfig for OpenInference instrumentors.
256
+
257
+ Returns:
258
+ List of successfully instrumented names.
259
+ """
260
+ skip_set = set(skip or [])
261
+
262
+ if names is None:
263
+ # Core set -- instruments all installed AI libraries automatically
264
+ target_names = [
265
+ "openai_agents",
266
+ "claude_agent_sdk",
267
+ "openai",
268
+ "anthropic",
269
+ "langchain",
270
+ "llama_index",
271
+ "litellm",
272
+ ]
273
+ else:
274
+ target_names = names
275
+
276
+ # Filter out skipped
277
+ target_names = [n for n in target_names if n not in skip_set]
278
+
279
+ instrumented = []
280
+ for name in target_names:
281
+ instrumentor = self.get(name)
282
+ if instrumentor is not None:
283
+ try:
284
+ instrumentor.instrument(trace_config=trace_config)
285
+ instrumented.append(name)
286
+ except Exception as e:
287
+ logger.debug("Failed to instrument %s: %s", name, e)
288
+ else:
289
+ # Skipped. Warn loudly if the AI library itself IS installed but
290
+ # its tracing instrumentor is missing — otherwise the user gets
291
+ # zero telemetry with no hint why (the classic silent-skip trap).
292
+ self.warn_if_lib_present_but_uninstrumented(name)
293
+
294
+ return instrumented
295
+
296
+ def warn_if_lib_present_but_uninstrumented(self, name: str) -> None:
297
+ """Warn when an installed AI library has no active instrumentor.
298
+
299
+ Only warns when the target library is actually importable — an absent
300
+ library has nothing to trace, so its skip is expected and silent.
301
+ """
302
+ instrumentor = self._instrumentors.get(name)
303
+ if instrumentor is None:
304
+ return
305
+ target = getattr(instrumentor, "target_package", None)
306
+ if not target or not _is_package_available(target):
307
+ return
308
+ instr_pkg = getattr(instrumentor, "package_name", None)
309
+ detail = f" ('{instr_pkg}')" if instr_pkg else ""
310
+ logger.warning(
311
+ "gatewaysdk.tracing: %r is installed but its tracing instrumentor%s "
312
+ "is not available, so its calls will NOT be traced. Install the "
313
+ "tracing extra to enable it: pip install 'gatewaysdk[tracing]'.",
314
+ target,
315
+ detail,
316
+ )
317
+
318
+ def uninstrument_all(self) -> None:
319
+ """Uninstrument all active instrumentors."""
320
+ for instrumentor in self._instrumentors.values():
321
+ try:
322
+ instrumentor.uninstrument()
323
+ except Exception as e:
324
+ logger.debug("Failed to uninstrument %s: %s", instrumentor.name, e)
325
+
326
+
327
+ def get_registry() -> InstrumentorRegistry:
328
+ """Get the global instrumentor registry.
329
+
330
+ Returns:
331
+ The singleton InstrumentorRegistry instance.
332
+ """
333
+ global _registry
334
+ if _registry is None:
335
+ _registry = InstrumentorRegistry()
336
+ return _registry
337
+
338
+
339
+ def _is_package_available(package_name: str) -> bool:
340
+ """Check if a package is available for import.
341
+
342
+ Args:
343
+ package_name: The package name (can be dotted path).
344
+
345
+ Returns:
346
+ True if the package is available.
347
+ """
348
+ try:
349
+ spec = importlib.util.find_spec(package_name)
350
+ return spec is not None
351
+ except (ModuleNotFoundError, ValueError):
352
+ return False