datadog 2.20.0 → 2.26.0

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 (310) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -1
  3. data/README.md +0 -1
  4. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +93 -23
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  8. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  9. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  10. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  12. data/ext/datadog_profiling_native_extension/http_transport.c +1 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  15. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  16. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  17. data/ext/libdatadog_api/ddsketch.c +106 -0
  18. data/ext/libdatadog_api/feature_flags.c +554 -0
  19. data/ext/libdatadog_api/feature_flags.h +5 -0
  20. data/ext/libdatadog_api/init.c +5 -0
  21. data/ext/libdatadog_api/library_config.c +34 -25
  22. data/ext/libdatadog_api/process_discovery.c +24 -18
  23. data/ext/libdatadog_extconf_helpers.rb +1 -1
  24. data/lib/datadog/ai_guard/api_client.rb +82 -0
  25. data/lib/datadog/ai_guard/component.rb +42 -0
  26. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  27. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  28. data/lib/datadog/ai_guard/configuration.rb +11 -0
  29. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  30. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  31. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  32. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  33. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  34. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  35. data/lib/datadog/ai_guard/ext.rb +16 -0
  36. data/lib/datadog/ai_guard.rb +153 -0
  37. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  38. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  39. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  40. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  41. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  42. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -5
  43. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  44. data/lib/datadog/appsec/assets/blocked.html +8 -0
  45. data/lib/datadog/appsec/assets/blocked.json +1 -1
  46. data/lib/datadog/appsec/assets/blocked.text +3 -1
  47. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  48. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  49. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  50. data/lib/datadog/appsec/assets.rb +1 -1
  51. data/lib/datadog/appsec/autoload.rb +1 -1
  52. data/lib/datadog/appsec/compressed_json.rb +1 -1
  53. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  54. data/lib/datadog/appsec/context.rb +2 -1
  55. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  56. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  57. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  58. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  59. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  60. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  61. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  62. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  63. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  64. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  65. data/lib/datadog/appsec/event.rb +12 -14
  66. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  67. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  68. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  69. data/lib/datadog/appsec/remote.rb +34 -25
  70. data/lib/datadog/appsec/response.rb +18 -4
  71. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  72. data/lib/datadog/appsec/security_engine/result.rb +29 -9
  73. data/lib/datadog/appsec/security_engine/runner.rb +19 -9
  74. data/lib/datadog/appsec/security_event.rb +5 -7
  75. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
  76. data/lib/datadog/core/configuration/components.rb +59 -11
  77. data/lib/datadog/core/configuration/config_helper.rb +100 -0
  78. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  79. data/lib/datadog/core/configuration/ext.rb +0 -1
  80. data/lib/datadog/core/configuration/option.rb +38 -43
  81. data/lib/datadog/core/configuration/option_definition.rb +4 -11
  82. data/lib/datadog/core/configuration/options.rb +9 -10
  83. data/lib/datadog/core/configuration/settings.rb +38 -9
  84. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  85. data/lib/datadog/core/configuration/supported_configurations.rb +373 -0
  86. data/lib/datadog/core/configuration.rb +2 -2
  87. data/lib/datadog/core/ddsketch.rb +19 -0
  88. data/lib/datadog/core/deprecations.rb +2 -2
  89. data/lib/datadog/core/environment/cgroup.rb +52 -25
  90. data/lib/datadog/core/environment/container.rb +140 -46
  91. data/lib/datadog/core/environment/ext.rb +7 -2
  92. data/lib/datadog/core/environment/git.rb +2 -2
  93. data/lib/datadog/core/environment/process.rb +87 -0
  94. data/lib/datadog/core/environment/variable_helpers.rb +3 -3
  95. data/lib/datadog/core/environment/yjit.rb +2 -1
  96. data/lib/datadog/core/error.rb +6 -6
  97. data/lib/datadog/core/feature_flags.rb +61 -0
  98. data/lib/datadog/core/metrics/client.rb +2 -2
  99. data/lib/datadog/core/pin.rb +8 -8
  100. data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
  101. data/lib/datadog/core/process_discovery.rb +48 -23
  102. data/lib/datadog/core/rate_limiter.rb +9 -1
  103. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  104. data/lib/datadog/core/remote/client.rb +14 -6
  105. data/lib/datadog/core/remote/component.rb +10 -10
  106. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  107. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  108. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  109. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  110. data/lib/datadog/core/remote/transport/config.rb +4 -25
  111. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  112. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  113. data/lib/datadog/core/remote/transport/http.rb +15 -24
  114. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  115. data/lib/datadog/core/remote/worker.rb +25 -37
  116. data/lib/datadog/core/runtime/ext.rb +0 -1
  117. data/lib/datadog/core/runtime/metrics.rb +11 -1
  118. data/lib/datadog/core/semaphore.rb +1 -4
  119. data/lib/datadog/core/tag_builder.rb +0 -4
  120. data/lib/datadog/core/tag_normalizer.rb +84 -0
  121. data/lib/datadog/core/telemetry/component.rb +69 -15
  122. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  123. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  124. data/lib/datadog/core/telemetry/event/app_started.rb +89 -51
  125. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  126. data/lib/datadog/core/telemetry/event.rb +1 -0
  127. data/lib/datadog/core/telemetry/logger.rb +2 -2
  128. data/lib/datadog/core/telemetry/logging.rb +2 -8
  129. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  130. data/lib/datadog/core/telemetry/request.rb +17 -3
  131. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  132. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  133. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  134. data/lib/datadog/core/telemetry/worker.rb +88 -32
  135. data/lib/datadog/core/transport/ext.rb +2 -0
  136. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  137. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  138. data/lib/datadog/core/transport/http/builder.rb +9 -5
  139. data/lib/datadog/core/transport/http/client.rb +80 -0
  140. data/lib/datadog/core/transport/http.rb +22 -19
  141. data/lib/datadog/core/transport/response.rb +15 -1
  142. data/lib/datadog/core/transport/transport.rb +90 -0
  143. data/lib/datadog/core/utils/array.rb +29 -0
  144. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  145. data/lib/datadog/core/utils/network.rb +22 -1
  146. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  147. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  148. data/lib/datadog/core/utils/sequence.rb +2 -0
  149. data/lib/datadog/core/utils/time.rb +1 -1
  150. data/lib/datadog/core/utils.rb +2 -0
  151. data/lib/datadog/core/workers/async.rb +10 -1
  152. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  153. data/lib/datadog/core/workers/polling.rb +2 -0
  154. data/lib/datadog/core/workers/queue.rb +100 -1
  155. data/lib/datadog/core.rb +2 -0
  156. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  157. data/lib/datadog/data_streams/configuration.rb +11 -0
  158. data/lib/datadog/data_streams/ext.rb +11 -0
  159. data/lib/datadog/data_streams/extensions.rb +16 -0
  160. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  161. data/lib/datadog/data_streams/processor.rb +509 -0
  162. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  163. data/lib/datadog/data_streams/transport/http.rb +40 -0
  164. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  165. data/lib/datadog/data_streams.rb +100 -0
  166. data/lib/datadog/di/boot.rb +7 -3
  167. data/lib/datadog/di/component.rb +14 -16
  168. data/lib/datadog/di/context.rb +70 -0
  169. data/lib/datadog/di/contrib/active_record.rb +30 -5
  170. data/lib/datadog/di/el/compiler.rb +168 -0
  171. data/lib/datadog/di/el/evaluator.rb +159 -0
  172. data/lib/datadog/di/el/expression.rb +42 -0
  173. data/lib/datadog/di/el.rb +5 -0
  174. data/lib/datadog/di/error.rb +34 -0
  175. data/lib/datadog/di/instrumenter.rb +189 -55
  176. data/lib/datadog/di/logger.rb +2 -2
  177. data/lib/datadog/di/probe.rb +55 -15
  178. data/lib/datadog/di/probe_builder.rb +41 -2
  179. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  180. data/lib/datadog/di/probe_file_loader.rb +1 -1
  181. data/lib/datadog/di/probe_manager.rb +50 -35
  182. data/lib/datadog/di/probe_notification_builder.rb +121 -70
  183. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  184. data/lib/datadog/di/proc_responder.rb +32 -0
  185. data/lib/datadog/di/remote.rb +89 -84
  186. data/lib/datadog/di/serializer.rb +151 -7
  187. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  188. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  189. data/lib/datadog/di/transport/http/input.rb +1 -33
  190. data/lib/datadog/di/transport/http.rb +32 -17
  191. data/lib/datadog/di/transport/input.rb +67 -34
  192. data/lib/datadog/di.rb +61 -5
  193. data/lib/datadog/error_tracking/filters.rb +2 -2
  194. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  195. data/lib/datadog/open_feature/component.rb +60 -0
  196. data/lib/datadog/open_feature/configuration.rb +27 -0
  197. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  198. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  199. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  200. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  201. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  202. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  203. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  204. data/lib/datadog/open_feature/ext.rb +14 -0
  205. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  206. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  207. data/lib/datadog/open_feature/provider.rb +141 -0
  208. data/lib/datadog/open_feature/remote.rb +67 -0
  209. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  210. data/lib/datadog/open_feature/transport.rb +70 -0
  211. data/lib/datadog/open_feature.rb +19 -0
  212. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  213. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  214. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  215. data/lib/datadog/opentelemetry/sdk/configurator.rb +26 -2
  216. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  217. data/lib/datadog/opentelemetry.rb +3 -0
  218. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  219. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  220. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  221. data/lib/datadog/profiling/collectors/info.rb +6 -5
  222. data/lib/datadog/profiling/component.rb +12 -11
  223. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  224. data/lib/datadog/profiling/ext.rb +2 -1
  225. data/lib/datadog/profiling/http_transport.rb +5 -2
  226. data/lib/datadog/profiling/profiler.rb +4 -0
  227. data/lib/datadog/profiling/tag_builder.rb +36 -3
  228. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  229. data/lib/datadog/profiling.rb +1 -2
  230. data/lib/datadog/single_step_instrument.rb +1 -1
  231. data/lib/datadog/tracing/component.rb +6 -17
  232. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  233. data/lib/datadog/tracing/configuration/ext.rb +9 -3
  234. data/lib/datadog/tracing/configuration/settings.rb +89 -10
  235. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  236. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  237. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  238. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  239. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  240. data/lib/datadog/tracing/contrib/component.rb +2 -2
  241. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  242. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  243. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  244. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  245. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  246. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  247. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  248. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +84 -43
  249. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  250. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  251. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  252. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  253. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  254. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  255. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  256. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  257. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  258. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  259. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
  260. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  261. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
  262. data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
  263. data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
  264. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  265. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  266. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  267. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  268. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
  269. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  270. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  271. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  272. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  273. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  274. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  275. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  276. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  277. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  278. data/lib/datadog/tracing/contrib/waterdrop.rb +41 -0
  279. data/lib/datadog/tracing/contrib.rb +1 -0
  280. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  281. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  282. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  283. data/lib/datadog/tracing/remote.rb +1 -9
  284. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  285. data/lib/datadog/tracing/span.rb +1 -1
  286. data/lib/datadog/tracing/span_event.rb +2 -2
  287. data/lib/datadog/tracing/span_operation.rb +20 -9
  288. data/lib/datadog/tracing/trace_operation.rb +44 -6
  289. data/lib/datadog/tracing/tracer.rb +42 -16
  290. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  291. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  292. data/lib/datadog/tracing/transport/http.rb +15 -9
  293. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  294. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  295. data/lib/datadog/tracing/transport/traces.rb +9 -71
  296. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  297. data/lib/datadog/tracing/writer.rb +1 -0
  298. data/lib/datadog/version.rb +2 -2
  299. data/lib/datadog.rb +3 -0
  300. metadata +110 -24
  301. data/ext/libdatadog_api/macos_development.md +0 -26
  302. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  303. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  304. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  305. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  306. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  307. data/lib/datadog/di/transport/http/api.rb +0 -42
  308. data/lib/datadog/di/transport/http/client.rb +0 -47
  309. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  310. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -11,7 +11,7 @@ static VALUE _native_configurator_get(VALUE self);
11
11
  static VALUE _native_configurator_with_local_path(DDTRACE_UNUSED VALUE _self, VALUE rb_configurator, VALUE path);
12
12
  static VALUE _native_configurator_with_fleet_path(DDTRACE_UNUSED VALUE _self, VALUE rb_configurator, VALUE path);
13
13
 
14
- static VALUE config_vec_class = Qnil;
14
+ static VALUE config_logged_result_class = Qnil;
15
15
 
16
16
  // ddog_Configurator memory management
17
17
  static void configurator_free(void *configurator_ptr) {
@@ -29,29 +29,29 @@ static const rb_data_type_t configurator_typed_data = {
29
29
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
30
30
  };
31
31
 
32
- // ddog_Vec_LibraryConfig memory management
33
- static void config_vec_free(void *config_vec_ptr) {
34
- ddog_Vec_LibraryConfig *config_vec = (ddog_Vec_LibraryConfig *)config_vec_ptr;
32
+ // ddog_LibraryConfigLoggedResult memory management
33
+ static void config_logged_result_free(void *config_logged_result_ptr) {
34
+ ddog_LibraryConfigLoggedResult *config_logged_result = (ddog_LibraryConfigLoggedResult *)config_logged_result_ptr;
35
35
 
36
- ddog_library_config_drop(*config_vec);
37
- ruby_xfree(config_vec_ptr);
36
+ ddog_library_config_drop(*config_logged_result);
37
+ ruby_xfree(config_logged_result_ptr);
38
38
  }
39
39
 
40
- static const rb_data_type_t config_vec_typed_data = {
41
- .wrap_struct_name = "Datadog::Core::Configuration::StableConfigVec",
40
+ static const rb_data_type_t config_logged_result_typed_data = {
41
+ .wrap_struct_name = "Datadog::Core::Configuration::StableConfigLoggedResult",
42
42
  .function = {
43
- .dfree = config_vec_free,
43
+ .dfree = config_logged_result_free,
44
44
  .dsize = NULL,
45
45
  },
46
46
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
47
47
  };
48
48
 
49
49
  void library_config_init(VALUE core_module) {
50
- rb_global_variable(&config_vec_class);
50
+ rb_global_variable(&config_logged_result_class);
51
51
  VALUE configuration_module = rb_define_module_under(core_module, "Configuration");
52
52
  VALUE stable_config_module = rb_define_module_under(configuration_module, "StableConfig");
53
53
  VALUE configurator_class = rb_define_class_under(stable_config_module, "Configurator", rb_cObject);
54
- config_vec_class = rb_define_class_under(configuration_module, "StableConfigVec", rb_cObject);
54
+ config_logged_result_class = rb_define_class_under(configuration_module, "StableConfigLoggedResult", rb_cObject);
55
55
 
56
56
  rb_define_alloc_func(configurator_class, _native_configurator_new);
57
57
  rb_define_method(configurator_class, "get", _native_configurator_get, 0);
@@ -61,11 +61,12 @@ void library_config_init(VALUE core_module) {
61
61
  rb_define_singleton_method(testing_module, "with_local_path", _native_configurator_with_local_path, 2);
62
62
  rb_define_singleton_method(testing_module, "with_fleet_path", _native_configurator_with_fleet_path, 2);
63
63
 
64
- rb_undef_alloc_func(config_vec_class); // It cannot be created from Ruby code and only serves as an intermediate object for the Ruby GC
64
+ rb_undef_alloc_func(config_logged_result_class); // It cannot be created from Ruby code and only serves as an intermediate object for the Ruby GC
65
65
  }
66
66
 
67
67
  static VALUE _native_configurator_new(VALUE klass) {
68
- ddog_Configurator *configurator = ddog_library_configurator_new(false, DDOG_CHARSLICE_C("ruby"));
68
+ // We always collect debug logs, so if DD_TRACE_DEBUG is set by stable config, we'll be able to log them.
69
+ ddog_Configurator *configurator = ddog_library_configurator_new(true, DDOG_CHARSLICE_C("ruby"));
69
70
 
70
71
  ddog_library_configurator_with_detect_process_info(configurator);
71
72
 
@@ -98,10 +99,11 @@ static VALUE _native_configurator_get(VALUE self) {
98
99
  ddog_Configurator *configurator;
99
100
  TypedData_Get_Struct(self, ddog_Configurator, &configurator_typed_data, configurator);
100
101
 
101
- ddog_Result_VecLibraryConfig configurator_result = ddog_library_configurator_get(configurator);
102
+ // We don't allocate memory here so if there is an error, we don't need to manage the memory
103
+ ddog_LibraryConfigLoggedResult before_error_result = ddog_library_configurator_get(configurator);
102
104
 
103
- if (configurator_result.tag == DDOG_RESULT_VEC_LIBRARY_CONFIG_ERR_VEC_LIBRARY_CONFIG) {
104
- ddog_Error err = configurator_result.err;
105
+ if (before_error_result.tag == DDOG_LIBRARY_CONFIG_LOGGED_RESULT_ERR) {
106
+ ddog_Error err = before_error_result.err;
105
107
  VALUE message = get_error_details_and_drop(&err);
106
108
  if (is_config_loaded()) {
107
109
  log_warning(message);
@@ -111,14 +113,20 @@ static VALUE _native_configurator_get(VALUE self) {
111
113
  return rb_hash_new();
112
114
  }
113
115
 
114
- // Wrapping config_vec into a Ruby object enables the Ruby GC to manage its memory
115
- // We need to allocate memory for config_vec because once it is out of scope, it will be freed (at the end of this function)
116
- // So we cannot reference it with &config_vec
116
+ // Wrapping config_logged_result into a Ruby object enables the Ruby GC to manage its memory
117
+ // We need to allocate memory for config_logged_result because once it is out of scope, it will be freed (at the end of this function)
117
118
  // We are doing this in case one of the ruby API raises an exception before the end of this function,
118
119
  // so the allocated memory will still be freed
119
- ddog_Vec_LibraryConfig *config_vec = ruby_xmalloc(sizeof(ddog_Vec_LibraryConfig));
120
- *config_vec = configurator_result.ok;
121
- VALUE config_vec_rb = TypedData_Wrap_Struct(config_vec_class, &config_vec_typed_data, config_vec);
120
+ ddog_LibraryConfigLoggedResult *configurator_logged_result = ruby_xcalloc(1, sizeof(ddog_LibraryConfigLoggedResult));
121
+ *configurator_logged_result = before_error_result;
122
+ VALUE config_logged_result_rb = TypedData_Wrap_Struct(config_logged_result_class, &config_logged_result_typed_data, configurator_logged_result);
123
+
124
+ VALUE logs = Qnil;
125
+ if (configurator_logged_result->ok.logs.length > 0) {
126
+ logs = rb_utf8_str_new_cstr(configurator_logged_result->ok.logs.ptr);
127
+ }
128
+
129
+ ddog_Vec_LibraryConfig config_vec = configurator_logged_result->ok.value;
122
130
 
123
131
  VALUE local_config_hash = rb_hash_new();
124
132
  VALUE fleet_config_hash = rb_hash_new();
@@ -127,8 +135,8 @@ static VALUE _native_configurator_get(VALUE self) {
127
135
  bool fleet_config_id_set = false;
128
136
  VALUE local_hash = rb_hash_new();
129
137
  VALUE fleet_hash = rb_hash_new();
130
- for (uintptr_t i = 0; i < config_vec->len; i++) {
131
- ddog_LibraryConfig config = config_vec->ptr[i];
138
+ for (uintptr_t i = 0; i < config_vec.len; i++) {
139
+ ddog_LibraryConfig config = config_vec.ptr[i];
132
140
  VALUE selected_hash;
133
141
  if (config.source == DDOG_LIBRARY_CONFIG_SOURCE_LOCAL_STABLE_CONFIG) {
134
142
  selected_hash = local_config_hash;
@@ -156,9 +164,10 @@ static VALUE _native_configurator_get(VALUE self) {
156
164
  rb_hash_aset(fleet_hash, ID2SYM(rb_intern("config")), fleet_config_hash);
157
165
 
158
166
  VALUE result = rb_hash_new();
167
+ rb_hash_aset(result, ID2SYM(rb_intern("logs")), logs);
159
168
  rb_hash_aset(result, ID2SYM(rb_intern("local")), local_hash);
160
169
  rb_hash_aset(result, ID2SYM(rb_intern("fleet")), fleet_hash);
161
170
 
162
- RB_GC_GUARD(config_vec_rb);
171
+ RB_GC_GUARD(config_logged_result_rb);
163
172
  return result;
164
173
  }
@@ -27,13 +27,13 @@ static const rb_data_type_t tracer_memfd_type = {
27
27
  };
28
28
 
29
29
  void process_discovery_init(VALUE core_module) {
30
- VALUE process_discovery_class = rb_define_class_under(core_module, "ProcessDiscovery", rb_cObject);
31
- VALUE tracer_memfd_class = rb_define_class_under(process_discovery_class, "TracerMemfd", rb_cObject);
30
+ VALUE process_discovery_module = rb_define_module_under(core_module, "ProcessDiscovery");
31
+ VALUE tracer_memfd_class = rb_define_class_under(process_discovery_module, "TracerMemfd", rb_cObject);
32
32
  rb_undef_alloc_func(tracer_memfd_class); // Class cannot be instantiated from Ruby
33
33
 
34
- rb_define_singleton_method(process_discovery_class, "_native_store_tracer_metadata", _native_store_tracer_metadata, -1);
35
- rb_define_singleton_method(process_discovery_class, "_native_to_rb_int", _native_to_rb_int, 1);
36
- rb_define_singleton_method(process_discovery_class, "_native_close_tracer_memfd", _native_close_tracer_memfd, 2);
34
+ rb_define_module_function(process_discovery_module, "_native_store_tracer_metadata", _native_store_tracer_metadata, -1);
35
+ rb_define_module_function(process_discovery_module, "_native_to_rb_int", _native_to_rb_int, 1);
36
+ rb_define_module_function(process_discovery_module, "_native_close_tracer_memfd", _native_close_tracer_memfd, 2);
37
37
  }
38
38
 
39
39
  static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
@@ -42,7 +42,6 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
42
42
  rb_scan_args(argc, argv, "1:", &logger, &options);
43
43
  if (options == Qnil) options = rb_hash_new();
44
44
 
45
- VALUE schema_version = rb_hash_fetch(options, ID2SYM(rb_intern("schema_version")));
46
45
  VALUE runtime_id = rb_hash_fetch(options, ID2SYM(rb_intern("runtime_id")));
47
46
  VALUE tracer_language = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_language")));
48
47
  VALUE tracer_version = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_version")));
@@ -50,8 +49,9 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
50
49
  VALUE service_name = rb_hash_fetch(options, ID2SYM(rb_intern("service_name")));
51
50
  VALUE service_env = rb_hash_fetch(options, ID2SYM(rb_intern("service_env")));
52
51
  VALUE service_version = rb_hash_fetch(options, ID2SYM(rb_intern("service_version")));
52
+ VALUE process_tags = rb_hash_fetch(options, ID2SYM(rb_intern("process_tags")));
53
+ VALUE container_id = rb_hash_fetch(options, ID2SYM(rb_intern("container_id")));
53
54
 
54
- ENFORCE_TYPE(schema_version, T_FIXNUM);
55
55
  ENFORCE_TYPE(runtime_id, T_STRING);
56
56
  ENFORCE_TYPE(tracer_language, T_STRING);
57
57
  ENFORCE_TYPE(tracer_version, T_STRING);
@@ -59,17 +59,23 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
59
59
  ENFORCE_TYPE(service_name, T_STRING);
60
60
  ENFORCE_TYPE(service_env, T_STRING);
61
61
  ENFORCE_TYPE(service_version, T_STRING);
62
-
63
- ddog_Result_TracerMemfdHandle result = ddog_store_tracer_metadata(
64
- (uint8_t) NUM2UINT(schema_version),
65
- char_slice_from_ruby_string(runtime_id),
66
- char_slice_from_ruby_string(tracer_language),
67
- char_slice_from_ruby_string(tracer_version),
68
- char_slice_from_ruby_string(hostname),
69
- char_slice_from_ruby_string(service_name),
70
- char_slice_from_ruby_string(service_env),
71
- char_slice_from_ruby_string(service_version)
72
- );
62
+ ENFORCE_TYPE(process_tags, T_STRING);
63
+ ENFORCE_TYPE(container_id, T_STRING);
64
+
65
+ void* builder = ddog_tracer_metadata_new();
66
+
67
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_RUNTIME_ID, StringValueCStr(runtime_id));
68
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_TRACER_LANGUAGE, StringValueCStr(tracer_language));
69
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_TRACER_VERSION, StringValueCStr(tracer_version));
70
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_HOSTNAME, StringValueCStr(hostname));
71
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_NAME, StringValueCStr(service_name));
72
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_ENV, StringValueCStr(service_env));
73
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_VERSION, StringValueCStr(service_version));
74
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_PROCESS_TAGS, StringValueCStr(process_tags));
75
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_CONTAINER_ID, StringValueCStr(container_id));
76
+
77
+ ddog_Result_TracerMemfdHandle result = ddog_tracer_metadata_store(builder);
78
+ ddog_tracer_metadata_free(builder);
73
79
 
74
80
  if (result.tag == DDOG_RESULT_TRACER_MEMFD_HANDLE_ERR_TRACER_MEMFD_HANDLE) {
75
81
  rb_funcall(logger, rb_intern("debug"), 1, rb_sprintf("Failed to store the tracer configuration in a memory file descriptor: %"PRIsVALUE, get_error_details_and_drop(&result.err)));
@@ -10,7 +10,7 @@ module Datadog
10
10
  module LibdatadogExtconfHelpers
11
11
  # Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
12
12
  # may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
13
- LIBDATADOG_VERSION = '~> 18.1.0.1.0'
13
+ LIBDATADOG_VERSION = '~> 25.0.0.1.0'
14
14
 
15
15
  # Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
16
16
  # libdatadog are moved after the extension gets compiled.
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "net/http"
5
+ require "json"
6
+
7
+ module Datadog
8
+ module AIGuard
9
+ # API Client for AI Guard API.
10
+ # Uses net/http to perform request. Raises on client and server errors.
11
+ class APIClient
12
+ DEFAULT_SITE = "app.datadoghq.com"
13
+ DEFAULT_PATH = "/api/v2/ai-guard"
14
+
15
+ def initialize(endpoint:, api_key:, application_key:, timeout:)
16
+ @timeout = timeout
17
+
18
+ @endpoint_uri = if endpoint
19
+ URI(endpoint) #: URI::HTTP
20
+ else
21
+ URI::HTTPS.build(
22
+ host: Datadog.configuration.site || DEFAULT_SITE,
23
+ path: DEFAULT_PATH
24
+ )
25
+ end
26
+
27
+ @headers = {
28
+ "DD-API-KEY": api_key.to_s,
29
+ "DD-APPLICATION-KEY": application_key.to_s,
30
+ "DD-AI-GUARD-VERSION": Datadog::VERSION::STRING,
31
+ "DD-AI-GUARD-SOURCE": "SDK",
32
+ "DD-AI-GUARD-LANGUAGE": "ruby",
33
+ "content-type": "application/json"
34
+ }.freeze
35
+ end
36
+
37
+ def post(path, body:)
38
+ Net::HTTP.start(@endpoint_uri.host.to_s, @endpoint_uri.port, use_ssl: use_ssl?, read_timeout: @timeout) do |http|
39
+ request = Net::HTTP::Post.new(@endpoint_uri.request_uri + path, @headers)
40
+ request.body = body.to_json
41
+
42
+ response = http.request(request)
43
+ raise_on_http_error!(response)
44
+
45
+ parse_response_body(response.body)
46
+ end
47
+ rescue Net::ReadTimeout
48
+ raise AIGuardClientError, "Request to AI Guard timed out"
49
+ end
50
+
51
+ private
52
+
53
+ def raise_on_http_error!(response)
54
+ case response
55
+ when Net::HTTPSuccess
56
+ # do nothing
57
+ when Net::HTTPRedirection
58
+ raise AIGuardClientError, "Redirects for AI Guard API are not supported"
59
+ else
60
+ error_message = begin
61
+ parsed_body = JSON.parse(response.body)
62
+ Array(parsed_body.fetch('errors')).join(', ')
63
+ rescue JSON::ParserError, KeyError
64
+ response.body
65
+ end
66
+
67
+ raise AIGuardClientError, error_message
68
+ end
69
+ end
70
+
71
+ def parse_response_body(body)
72
+ JSON.parse(body)
73
+ rescue JSON::ParserError
74
+ raise AIGuardClientError, "Could not parse response body"
75
+ end
76
+
77
+ def use_ssl?
78
+ @endpoint_uri.scheme == 'https'
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_client'
4
+ require_relative 'evaluation'
5
+ require_relative 'evaluation/request'
6
+ require_relative 'evaluation/result'
7
+ require_relative 'evaluation/no_op_result'
8
+ require_relative 'evaluation/message'
9
+ require_relative 'evaluation/tool_call'
10
+ require_relative 'ext'
11
+
12
+ module Datadog
13
+ module AIGuard
14
+ # Component for API Guard product
15
+ class Component
16
+ attr_reader :api_client, :logger
17
+
18
+ def self.build(settings, logger:, telemetry:)
19
+ return unless settings.respond_to?(:ai_guard) && settings.ai_guard.enabled
20
+
21
+ api_client = APIClient.new(
22
+ endpoint: settings.ai_guard.endpoint,
23
+ api_key: settings.api_key,
24
+ application_key: settings.ai_guard.app_key,
25
+ timeout: settings.ai_guard.timeout_ms / 1_000
26
+ )
27
+
28
+ new(api_client, logger: logger, telemetry: telemetry)
29
+ end
30
+
31
+ def initialize(api_client, logger:, telemetry:)
32
+ @api_client = api_client
33
+ @logger = logger
34
+ @telemetry = telemetry
35
+ end
36
+
37
+ def shutdown!
38
+ # no-op
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Configuration
6
+ # This module contains constants for AI Guard component
7
+ module Ext
8
+ ENV_AI_GUARD_ENABLED = "DD_AI_GUARD_ENABLED"
9
+ ENV_AI_GUARD_ENDPOINT = "DD_AI_GUARD_ENDPOINT"
10
+ ENV_AI_GUARD_TIMEOUT = "DD_AI_GUARD_TIMEOUT"
11
+ ENV_AI_GUARD_MAX_CONTENT_SIZE = "DD_AI_GUARD_MAX_CONTENT_SIZE"
12
+ ENV_AI_GUARD_MAX_MESSAGES_LENGTH = "DD_AI_GUARD_MAX_MESSAGES_LENGTH"
13
+ ENV_APP_KEY = "DD_APP_KEY"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require_relative "ext"
5
+
6
+ module Datadog
7
+ module AIGuard
8
+ module Configuration
9
+ # AI Guard specific settings
10
+ module Settings
11
+ def self.extended(base)
12
+ base = base.singleton_class unless base.is_a?(Class)
13
+ add_settings!(base)
14
+ end
15
+
16
+ def self.add_settings!(base)
17
+ base.class_eval do
18
+ # AI Guard specific configurations.
19
+ # @public_api
20
+ settings :ai_guard do
21
+ # Enable AI Guard.
22
+ #
23
+ # You can use this option to skip calls to AI Guard API without having to remove library as a whole.
24
+ #
25
+ # @default `DD_AI_GUARD_ENABLED`, otherwise `false`
26
+ # @return [Boolean]
27
+ option :enabled do |o|
28
+ o.type :bool
29
+ o.env Ext::ENV_AI_GUARD_ENABLED
30
+ o.default false
31
+ end
32
+
33
+ # AI Guard API endpoint path.
34
+ #
35
+ # @default `DD_AI_GUARD_ENDPOINT`, otherwise `nil`
36
+ # @return [String, nil]
37
+ option :endpoint do |o|
38
+ o.type :string, nilable: true
39
+ o.env Ext::ENV_AI_GUARD_ENDPOINT
40
+
41
+ o.setter do |value|
42
+ next unless value
43
+
44
+ uri = URI(value.to_s)
45
+ raise ArgumentError, "Please provide an absolute URI that includes a protocol" unless uri.absolute?
46
+
47
+ uri.to_s.delete_suffix("/")
48
+ end
49
+ end
50
+
51
+ # Datadog Application key.
52
+ #
53
+ # @default `DD_APP_KEY` environment variable, otherwise `nil`
54
+ # @return [String, nil]
55
+ option :app_key do |o|
56
+ o.type :string, nilable: true
57
+ o.env Ext::ENV_APP_KEY
58
+ end
59
+
60
+ # Request timeout in milliseconds.
61
+ #
62
+ # @default `DD_AI_GUARD_TIMEOUT`, otherwise 10 000 ms
63
+ # @return [Integer]
64
+ option :timeout_ms do |o|
65
+ o.type :int
66
+ o.env Ext::ENV_AI_GUARD_TIMEOUT
67
+ o.default 10_000
68
+ end
69
+
70
+ # Maximum content size in bytes.
71
+ # Content that exceeds the maximum allowed size is truncated before
72
+ # being stored in the current span context.
73
+ #
74
+ # @default `DD_AI_GUARD_MAX_CONTENT_SIZE`, otherwise 524 228 bytes
75
+ # @return [Integer]
76
+ option :max_content_size_bytes do |o|
77
+ o.type :int
78
+ o.env Ext::ENV_AI_GUARD_MAX_CONTENT_SIZE
79
+ o.default 512 * 1024
80
+ end
81
+
82
+ # Maximum number of messages.
83
+ # Older messages are omitted once the message limit is reached.
84
+ #
85
+ # @default `DD_AI_GUARD_MAX_MESSAGES_LENGTH`, otherwise 16 messages
86
+ # @return [Integer]
87
+ option :max_messages_length do |o|
88
+ o.type :int
89
+ o.env Ext::ENV_AI_GUARD_MAX_MESSAGES_LENGTH
90
+ o.default 16
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ # Configuration module for AI Guard
6
+ module Configuration
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative "configuration/settings"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Evaluation
6
+ # Message class for AI Guard
7
+ class Message
8
+ attr_reader :role, :content, :tool_call, :tool_call_id
9
+
10
+ def initialize(role:, content: nil, tool_call: nil, tool_call_id: nil)
11
+ raise ArgumentError, "Role must be set to a non-empty value" if role.to_s.empty?
12
+
13
+ @role = role.to_sym
14
+ @content = content
15
+ @tool_call = tool_call
16
+ @tool_call_id = tool_call_id
17
+
18
+ if @tool_call && !@tool_call.is_a?(ToolCall)
19
+ raise ArgumentError, "Expected an instance of #{ToolCall.name} for :tool_call argument"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Evaluation
6
+ # Class for emulating AI Guard evaluation result when AI Guard is disabled.
7
+ class NoOpResult
8
+ attr_reader :action, :reason, :tags
9
+
10
+ def initialize
11
+ @action = Result::ALLOW_ACTION
12
+ @reason = "AI Guard is disabled"
13
+ @tags = []
14
+ end
15
+
16
+ def allow?
17
+ true
18
+ end
19
+
20
+ def deny?
21
+ false
22
+ end
23
+
24
+ def abort?
25
+ false
26
+ end
27
+
28
+ def blocking_enabled?
29
+ false
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Evaluation
6
+ # Request builds the request body from an array of messages and processes the response
7
+ class Request
8
+ REQUEST_PATH = "/evaluate"
9
+
10
+ attr_reader :serialized_messages
11
+
12
+ def initialize(messages)
13
+ @serialized_messages = serialize_messages(messages)
14
+ end
15
+
16
+ def perform
17
+ api_client = AIGuard.api_client
18
+
19
+ # This should never happen, as we are only calling this method when AI Guard is enabled,
20
+ # and this means the API Client was not initialized properly.
21
+ #
22
+ # Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug
23
+ raise "AI Guard API Client not initialized" unless api_client
24
+
25
+ raw_response = api_client.post(REQUEST_PATH, body: build_request_body)
26
+
27
+ Result.new(raw_response)
28
+ end
29
+
30
+ private
31
+
32
+ def build_request_body
33
+ {
34
+ data: {
35
+ attributes: {
36
+ messages: @serialized_messages,
37
+ meta: {
38
+ service: Datadog.configuration.service,
39
+ env: Datadog.configuration.env
40
+ }
41
+ }
42
+ }
43
+ }
44
+ end
45
+
46
+ def serialize_messages(messages)
47
+ serialized_messages = []
48
+
49
+ messages.each do |message|
50
+ serialized_messages << serialize_message(message)
51
+
52
+ break if serialized_messages.count == Datadog.configuration.ai_guard.max_messages_length
53
+ end
54
+
55
+ serialized_messages
56
+ end
57
+
58
+ def serialize_message(message)
59
+ if message.tool_call
60
+ {
61
+ role: message.role,
62
+ tool_calls: [
63
+ {
64
+ id: message.tool_call.id,
65
+ function: {
66
+ name: message.tool_call.tool_name,
67
+ arguments: message.tool_call.arguments
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ elsif message.tool_call_id
73
+ {role: message.role, tool_call_id: message.tool_call_id, content: message.content}
74
+ else
75
+ {role: message.role, content: message.content}
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Evaluation
6
+ # Wrapper class for evaluation API response
7
+ class Result
8
+ ALLOW_ACTION = "ALLOW"
9
+ DENY_ACTION = "DENY"
10
+ ABORT_ACTION = "ABORT"
11
+
12
+ attr_reader :action, :reason, :tags
13
+
14
+ def initialize(raw_response)
15
+ attributes = raw_response.fetch("data").fetch("attributes")
16
+
17
+ @action = attributes.fetch("action")
18
+ @reason = attributes.fetch("reason")
19
+ @tags = attributes.fetch("tags")
20
+ @is_blocking_enabled = attributes.fetch("is_blocking_enabled")
21
+ rescue KeyError => e
22
+ raise AIGuardClientError, "Missing key: \"#{e.key}\""
23
+ end
24
+
25
+ def allow?
26
+ action == ALLOW_ACTION
27
+ end
28
+
29
+ def deny?
30
+ action == DENY_ACTION
31
+ end
32
+
33
+ def abort?
34
+ action == ABORT_ACTION
35
+ end
36
+
37
+ def blocking_enabled?
38
+ !!@is_blocking_enabled
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AIGuard
5
+ module Evaluation
6
+ # Tool call class for AI Guard
7
+ class ToolCall
8
+ attr_reader :tool_name, :id, :arguments
9
+
10
+ def initialize(tool_name, id:, arguments:)
11
+ @tool_name = tool_name
12
+ @id = id
13
+ @arguments = arguments
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end