ruby_llm-instrumentation 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67942ad10a363e6e530277085f6750ea53eb547e88df806f19ddbbebef286adc
4
- data.tar.gz: 4a2208687976b6744d1cbe6e19aa03d0a5bf9d080f4597ad1341e8d9cf590a4b
3
+ metadata.gz: 40d0077ab75737f0cc26a40df7249d3a1c0b2907504adbf753dc1b5d97dc249c
4
+ data.tar.gz: eb7d1694584584c1f0e9cea0fe8161e53e34e02a9f38aa6676cb2c66d7c4ae26
5
5
  SHA512:
6
- metadata.gz: f9190dea5c2c5d4c8824f4e93c278a527c770204370b93a0e69d622cd09369597674ceb1dc6fa72e499b5e6858afb95a83d1582d8ed67fa9cac44b53fd94e95f
7
- data.tar.gz: 254a5922c0a6fa80c8d907de2575e8d43cff803f0d4c9c1892d3285ef16159bf53abb61e73a49552345bdd83566b9837c5509327cad344cce9f99e41bfce2ddf
6
+ metadata.gz: 77b54c9936dc4fe603c59c1525202af80d31c317fdf9ea22fe3b04914c76c501bfbdb5ae1785a2a314ee57da65685184ddcb680d9f5c39d3e53460bd806d56bd
7
+ data.tar.gz: 1885dec884b5396c1ff055af82a5c40253e3339f53bd2f02c43945d6304be1176cac2d95135662627d6551ea29a5c7efe4d2d660362ad0effecbdaf7bdf013ce
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-01-20
9
+
10
+ ### Added
11
+
12
+ - Custom metadata support via `RubyLLM::Instrumentation.with` block method. [#10](https://github.com/sinaptia/ruby_llm-instrumentation/pull/10) [@marckohlbrugge](https://github.com/marckohlbrugge)
data/README.md CHANGED
@@ -20,7 +20,9 @@ Now, RubyLLM will instrument all calls to your configured LLM.
20
20
 
21
21
  ## Usage
22
22
 
23
- RubyLLM::Instrumentation uses ActiveSupport::Notifications to publish events. You can subscribe to these events to build custom monitoring, logging, or analytics:
23
+ RubyLLM::Instrumentation uses ActiveSupport::Notifications to publish events. You can subscribe to these events to build custom monitoring, logging, or analytics.
24
+
25
+ ### Subscribing to events
24
26
 
25
27
  ```ruby
26
28
  # Subscribe to all LLM events
@@ -31,6 +33,48 @@ ActiveSupport::Notifications.subscribe(/ruby_llm/) do |name, start, finish, id,
31
33
  Rails.logger.info "Duration: #{duration}ms"
32
34
  Rails.logger.info "Input tokens: #{payload[:input_tokens]}"
33
35
  Rails.logger.info "Output tokens: #{payload[:output_tokens]}"
36
+ Rails.logger.info "Metadata: #{payload[:metadata]}" if payload[:metadata]
37
+ end
38
+ ```
39
+
40
+ ### Metadata
41
+
42
+ You can attach custom metadata to any RubyLLM call for tracking, attribution, or analytics purposes. Metadata is included in the event payload under the `metadata` key.
43
+
44
+ ```ruby
45
+ # Block form - great for controller around_actions
46
+ RubyLLM::Instrumentation.with(user_id: current_user.id, feature: "chat_assistant") do
47
+ RubyLLM.chat.ask("Hello")
48
+ end
49
+
50
+ # One-liners work too
51
+ RubyLLM::Instrumentation.with(feature: "search") { RubyLLM.embed("text") }
52
+ ```
53
+
54
+ The block form is particularly useful for setting request-level context:
55
+
56
+ ```ruby
57
+ class ApplicationController < ActionController::Base
58
+ around_action :instrument_llm_calls
59
+
60
+ private
61
+
62
+ def instrument_llm_calls
63
+ RubyLLM::Instrumentation.with(user_id: current_user&.id, request_id: request.uuid) do
64
+ yield
65
+ end
66
+ end
67
+ end
68
+ ```
69
+
70
+ Nested blocks merge metadata, so you can set global context and add specific metadata per-call:
71
+
72
+ ```ruby
73
+ RubyLLM::Instrumentation.with(user_id: 123) do
74
+ RubyLLM::Instrumentation.with(feature: "chat") do
75
+ RubyLLM.chat.ask("Hello")
76
+ # metadata: { user_id: 123, feature: "chat" }
77
+ end
34
78
  end
35
79
  ```
36
80
 
@@ -53,6 +97,7 @@ Triggered when `#ask` is called.
53
97
  | output_tokens | Output tokens consumed |
54
98
  | cached_tokens | Cache reads tokens (if supported) |
55
99
  | cache_creation_tokens | Cache write tokens (if supported) |
100
+ | metadata | Custom metadata hash (if provided) |
56
101
 
57
102
  #### execute_tool.ruby_llm
58
103
 
@@ -67,6 +112,7 @@ Triggered when `#execute_tool` is called.
67
112
  | arguments | The arguments |
68
113
  | chat | The chat, a RubyLLM::Chat instance |
69
114
  | halted | Indicates if the tool stopped the conversation loop |
115
+ | metadata | Custom metadata hash (if provided) |
70
116
 
71
117
  ### RubyLLM::Embedding
72
118
 
@@ -82,6 +128,7 @@ Triggered when `.embed` is called.
82
128
  | dimensions | Number of embedding dimensions (or array of sizes if multiple) |
83
129
  | input_tokens | Input tokens consumed |
84
130
  | vector_count | Number of vectors generated |
131
+ | metadata | Custom metadata hash (if provided) |
85
132
 
86
133
  ### RubyLLM::Image
87
134
 
@@ -93,8 +140,9 @@ Triggered when `.paint` is called.
93
140
  | -------- | -------------------------------------------- |
94
141
  | provider | Provider slug |
95
142
  | size | Image dimensions |
96
- | image | The inage generated, a RubyLLM::Image object |
143
+ | image | The image generated, a RubyLLM::Image object |
97
144
  | model | Model ID |
145
+ | metadata | Custom metadata hash (if provided) |
98
146
 
99
147
  ### RubyLLM::Moderation
100
148
 
@@ -108,6 +156,7 @@ Triggered when `.moderate` is called.
108
156
  | moderation | The moderation, a RubyLLM::Moderation object |
109
157
  | model | Model ID |
110
158
  | flagged | Whether the text was flagged |
159
+ | metadata | Custom metadata hash (if provided) |
111
160
 
112
161
  ### RubyLLM::Transcription
113
162
 
@@ -123,6 +172,7 @@ Triggered when `.transcribe` is called.
123
172
  | input_tokens | Input tokens consumed |
124
173
  | output_tokens | Output tokens consumed |
125
174
  | duration | Audio duration in seconds (if available) |
175
+ | metadata | Custom metadata hash (if provided) |
126
176
 
127
177
  ## Contributing
128
178
 
@@ -9,8 +9,9 @@ module RubyLLM
9
9
  raw_payload = {
10
10
  provider: @provider.slug,
11
11
  model: @model.id,
12
- streaming: block_given?
13
- }
12
+ streaming: block_given?,
13
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
14
+ }.compact
14
15
 
15
16
  ActiveSupport::Notifications.instrument("complete_chat.ruby_llm", raw_payload) do |payload|
16
17
  original_complete(&).tap do |response|
@@ -32,8 +33,9 @@ module RubyLLM
32
33
  model: @model.id,
33
34
  tool_call: tool_call,
34
35
  tool_name: tool_call.name,
35
- arguments: tool_call.arguments
36
- }
36
+ arguments: tool_call.arguments,
37
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
38
+ }.compact
37
39
 
38
40
  ActiveSupport::Notifications.instrument("execute_tool.ruby_llm", raw_payload) do |payload|
39
41
  original_execute_tool(tool_call).tap do |response|
@@ -8,8 +8,9 @@ module RubyLLM
8
8
  alias_method :original_embed, :embed
9
9
  def embed(text, model: nil, provider: nil, assume_model_exists: false, context: nil, dimensions: nil)
10
10
  raw_payload = {
11
- provider:
12
- }
11
+ provider:,
12
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
13
+ }.compact
13
14
 
14
15
  ActiveSupport::Notifications.instrument("embed_text.ruby_llm", raw_payload) do |payload|
15
16
  original_embed(text, model:, provider:, assume_model_exists:, context:, dimensions:).tap do |response|
@@ -9,8 +9,9 @@ module RubyLLM
9
9
  def paint(prompt, model: nil, provider: nil, assume_model_exists: false, size: "1024x1024", context: nil)
10
10
  raw_payload = {
11
11
  provider:,
12
- size:
13
- }
12
+ size:,
13
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
14
+ }.compact
14
15
 
15
16
  ActiveSupport::Notifications.instrument("paint_image.ruby_llm", raw_payload) do |payload|
16
17
  original_paint(prompt, model:, provider:, assume_model_exists:, size:, context:).tap do |response|
@@ -8,8 +8,9 @@ module RubyLLM
8
8
  alias_method :original_moderate, :moderate
9
9
  def moderate(input, model: nil, provider: nil, assume_model_exists: false, context: nil)
10
10
  raw_payload = {
11
- provider:
12
- }
11
+ provider:,
12
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
13
+ }.compact
13
14
 
14
15
  ActiveSupport::Notifications.instrument("moderate_text.ruby_llm", raw_payload) do |payload|
15
16
  original_moderate(input, model:, provider:, assume_model_exists:, context:).tap do |response|
@@ -8,8 +8,9 @@ module RubyLLM
8
8
  alias_method :original_transcribe, :transcribe
9
9
  def transcribe(audio_file, **kwargs)
10
10
  raw_payload = {
11
- provider: kwargs[:provider]
12
- }
11
+ provider: kwargs[:provider],
12
+ metadata: RubyLLM::Instrumentation.current_metadata.presence
13
+ }.compact
13
14
 
14
15
  ActiveSupport::Notifications.instrument("transcribe_audio.ruby_llm", raw_payload) do |payload|
15
16
  original_transcribe(audio_file, **kwargs).tap do |response|
@@ -1,5 +1,5 @@
1
1
  module RubyLLM
2
2
  module Instrumentation
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -4,11 +4,27 @@ require "ruby_llm/instrumentation/railtie"
4
4
 
5
5
  module RubyLLM
6
6
  module Instrumentation
7
+ METADATA_KEY = :ruby_llm_instrumentation_metadata
8
+
7
9
  autoload :Chat, "ruby_llm/instrumentation/chat"
8
10
  autoload :Embedding, "ruby_llm/instrumentation/embedding"
9
11
  autoload :Image, "ruby_llm/instrumentation/image"
10
12
  autoload :Transcription, "ruby_llm/instrumentation/transcription"
11
13
  autoload :Moderation, "ruby_llm/instrumentation/moderation"
14
+
15
+ class << self
16
+ def with(metadata = {})
17
+ previous_metadata = Thread.current[METADATA_KEY]
18
+ Thread.current[METADATA_KEY] = (previous_metadata || {}).merge(metadata)
19
+ yield
20
+ ensure
21
+ Thread.current[METADATA_KEY] = previous_metadata
22
+ end
23
+
24
+ def current_metadata
25
+ Thread.current[METADATA_KEY] || {}
26
+ end
27
+ end
12
28
  end
13
29
  end
14
30
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-instrumentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patricio Mac Adden
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 7.0.0
18
+ version: 7.2.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 7.0.0
25
+ version: 7.2.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ruby_llm
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -44,6 +44,7 @@ executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
+ - CHANGELOG.md
47
48
  - README.md
48
49
  - Rakefile
49
50
  - lib/ruby_llm/instrumentation.rb
@@ -60,6 +61,7 @@ licenses: []
60
61
  metadata:
61
62
  homepage_uri: https://github.com/sinaptia/ruby_llm-instrumentation
62
63
  source_code_uri: https://github.com/sinaptia/ruby_llm-instrumentation
64
+ changelog_uri: https://github.com/sinaptia/ruby_llm-instrumentation/blob/main/CHANGELOG.md
63
65
  rdoc_options: []
64
66
  require_paths:
65
67
  - lib