ruby_llm-instrumentation 0.1.1
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 +7 -0
- data/README.md +133 -0
- data/Rakefile +3 -0
- data/lib/ruby_llm/instrumentation/chat.rb +49 -0
- data/lib/ruby_llm/instrumentation/embedding.rb +30 -0
- data/lib/ruby_llm/instrumentation/image.rb +26 -0
- data/lib/ruby_llm/instrumentation/moderation.rb +26 -0
- data/lib/ruby_llm/instrumentation/railtie.rb +23 -0
- data/lib/ruby_llm/instrumentation/transcription.rb +29 -0
- data/lib/ruby_llm/instrumentation/version.rb +5 -0
- data/lib/ruby_llm/instrumentation.rb +19 -0
- data/lib/tasks/ruby_llm/instrumentation_tasks.rake +4 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 67942ad10a363e6e530277085f6750ea53eb547e88df806f19ddbbebef286adc
|
|
4
|
+
data.tar.gz: 4a2208687976b6744d1cbe6e19aa03d0a5bf9d080f4597ad1341e8d9cf590a4b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f9190dea5c2c5d4c8824f4e93c278a527c770204370b93a0e69d622cd09369597674ceb1dc6fa72e499b5e6858afb95a83d1582d8ed67fa9cac44b53fd94e95f
|
|
7
|
+
data.tar.gz: 254a5922c0a6fa80c8d907de2575e8d43cff803f0d4c9c1892d3285ef16159bf53abb61e73a49552345bdd83566b9837c5509327cad344cce9f99e41bfce2ddf
|
data/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# RubyLLM::Instrumentation
|
|
2
|
+
|
|
3
|
+
Rails instrumentation for RubyLLM.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "ruby_llm-instrumentation"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Now, RubyLLM will instrument all calls to your configured LLM.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
RubyLLM::Instrumentation uses ActiveSupport::Notifications to publish events. You can subscribe to these events to build custom monitoring, logging, or analytics:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# Subscribe to all LLM events
|
|
27
|
+
ActiveSupport::Notifications.subscribe(/ruby_llm/) do |name, start, finish, id, payload|
|
|
28
|
+
duration = finish - start
|
|
29
|
+
|
|
30
|
+
Rails.logger.info "LLM Call: #{payload[:provider]}/#{payload[:model]}"
|
|
31
|
+
Rails.logger.info "Duration: #{duration}ms"
|
|
32
|
+
Rails.logger.info "Input tokens: #{payload[:input_tokens]}"
|
|
33
|
+
Rails.logger.info "Output tokens: #{payload[:output_tokens]}"
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Available Events
|
|
38
|
+
|
|
39
|
+
### RubyLLM::Chat
|
|
40
|
+
|
|
41
|
+
#### complete_chat.ruby_llm
|
|
42
|
+
|
|
43
|
+
Triggered when `#ask` is called.
|
|
44
|
+
|
|
45
|
+
| Key | Value |
|
|
46
|
+
| --------------------- | --------------------------------------- |
|
|
47
|
+
| provider | Provider slug |
|
|
48
|
+
| model | Model ID |
|
|
49
|
+
| streaming | Whether streaming was used |
|
|
50
|
+
| chat | The chat, a RubyLLM::Chat object |
|
|
51
|
+
| response | The response, a RubyLLM::Message object |
|
|
52
|
+
| input_tokens | Input tokens consumed |
|
|
53
|
+
| output_tokens | Output tokens consumed |
|
|
54
|
+
| cached_tokens | Cache reads tokens (if supported) |
|
|
55
|
+
| cache_creation_tokens | Cache write tokens (if supported) |
|
|
56
|
+
|
|
57
|
+
#### execute_tool.ruby_llm
|
|
58
|
+
|
|
59
|
+
Triggered when `#execute_tool` is called.
|
|
60
|
+
|
|
61
|
+
| Key | Value |
|
|
62
|
+
| --------- | --------------------------------------------------- |
|
|
63
|
+
| provider | Provider slug |
|
|
64
|
+
| model | Model ID |
|
|
65
|
+
| tool_call | The tool call, a RubyLLM::ToolCall object |
|
|
66
|
+
| tool_name | The tool name |
|
|
67
|
+
| arguments | The arguments |
|
|
68
|
+
| chat | The chat, a RubyLLM::Chat instance |
|
|
69
|
+
| halted | Indicates if the tool stopped the conversation loop |
|
|
70
|
+
|
|
71
|
+
### RubyLLM::Embedding
|
|
72
|
+
|
|
73
|
+
#### embed_text.ruby_llm
|
|
74
|
+
|
|
75
|
+
Triggered when `.embed` is called.
|
|
76
|
+
|
|
77
|
+
| Key | Value |
|
|
78
|
+
| ------------ | -------------------------------------------------------------- |
|
|
79
|
+
| provider | Provider slug |
|
|
80
|
+
| embedding | The embedding, a RubyLLM::Embedding object |
|
|
81
|
+
| model | Model ID |
|
|
82
|
+
| dimensions | Number of embedding dimensions (or array of sizes if multiple) |
|
|
83
|
+
| input_tokens | Input tokens consumed |
|
|
84
|
+
| vector_count | Number of vectors generated |
|
|
85
|
+
|
|
86
|
+
### RubyLLM::Image
|
|
87
|
+
|
|
88
|
+
#### paint_image.ruby_llm
|
|
89
|
+
|
|
90
|
+
Triggered when `.paint` is called.
|
|
91
|
+
|
|
92
|
+
| Key | Value |
|
|
93
|
+
| -------- | -------------------------------------------- |
|
|
94
|
+
| provider | Provider slug |
|
|
95
|
+
| size | Image dimensions |
|
|
96
|
+
| image | The inage generated, a RubyLLM::Image object |
|
|
97
|
+
| model | Model ID |
|
|
98
|
+
|
|
99
|
+
### RubyLLM::Moderation
|
|
100
|
+
|
|
101
|
+
#### moderate_text.ruby_llm
|
|
102
|
+
|
|
103
|
+
Triggered when `.moderate` is called.
|
|
104
|
+
|
|
105
|
+
| Key | Value |
|
|
106
|
+
| ---------- | -------------------------------------------- |
|
|
107
|
+
| provider | Provider slug |
|
|
108
|
+
| moderation | The moderation, a RubyLLM::Moderation object |
|
|
109
|
+
| model | Model ID |
|
|
110
|
+
| flagged | Whether the text was flagged |
|
|
111
|
+
|
|
112
|
+
### RubyLLM::Transcription
|
|
113
|
+
|
|
114
|
+
#### transcribe_audio.ruby_llm
|
|
115
|
+
|
|
116
|
+
Triggered when `.transcribe` is called.
|
|
117
|
+
|
|
118
|
+
| Key | Value |
|
|
119
|
+
| ------------- | -------------------------------------------------- |
|
|
120
|
+
| provider | Provider slug |
|
|
121
|
+
| transcription | The transcription, a RubyLLM::Transcription object |
|
|
122
|
+
| model | Model ID |
|
|
123
|
+
| input_tokens | Input tokens consumed |
|
|
124
|
+
| output_tokens | Output tokens consumed |
|
|
125
|
+
| duration | Audio duration in seconds (if available) |
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
You can open an issue or a PR in GitHub.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
module Chat
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
alias_method :original_complete, :complete
|
|
8
|
+
def complete(&)
|
|
9
|
+
raw_payload = {
|
|
10
|
+
provider: @provider.slug,
|
|
11
|
+
model: @model.id,
|
|
12
|
+
streaming: block_given?
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
ActiveSupport::Notifications.instrument("complete_chat.ruby_llm", raw_payload) do |payload|
|
|
16
|
+
original_complete(&).tap do |response|
|
|
17
|
+
payload[:response] = response
|
|
18
|
+
%i[input_tokens output_tokens cached_tokens cache_creation_tokens].each do |field|
|
|
19
|
+
value = response.public_send(field)
|
|
20
|
+
payload[field] = value unless value.nil?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
ensure
|
|
24
|
+
payload[:chat] = self
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
alias_method :original_execute_tool, :execute_tool
|
|
29
|
+
def execute_tool(tool_call)
|
|
30
|
+
raw_payload = {
|
|
31
|
+
provider: @provider.slug,
|
|
32
|
+
model: @model.id,
|
|
33
|
+
tool_call: tool_call,
|
|
34
|
+
tool_name: tool_call.name,
|
|
35
|
+
arguments: tool_call.arguments
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ActiveSupport::Notifications.instrument("execute_tool.ruby_llm", raw_payload) do |payload|
|
|
39
|
+
original_execute_tool(tool_call).tap do |response|
|
|
40
|
+
payload[:halted] = response.is_a?(Tool::Halt)
|
|
41
|
+
end
|
|
42
|
+
ensure
|
|
43
|
+
payload[:chat] = self
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
module Embedding
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class << self
|
|
8
|
+
alias_method :original_embed, :embed
|
|
9
|
+
def embed(text, model: nil, provider: nil, assume_model_exists: false, context: nil, dimensions: nil)
|
|
10
|
+
raw_payload = {
|
|
11
|
+
provider:
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
ActiveSupport::Notifications.instrument("embed_text.ruby_llm", raw_payload) do |payload|
|
|
15
|
+
original_embed(text, model:, provider:, assume_model_exists:, context:, dimensions:).tap do |response|
|
|
16
|
+
payload[:embedding] = response
|
|
17
|
+
payload[:model] = response.model
|
|
18
|
+
payload[:input_tokens] = response.input_tokens unless response.input_tokens.nil?
|
|
19
|
+
|
|
20
|
+
# response.vectors is an array of floats, or an array of an array of floats
|
|
21
|
+
payload[:vector_count] = response.vectors.first.is_a?(Array) ? response.vectors.size : 1
|
|
22
|
+
payload[:dimensions] = response.vectors.first.is_a?(Array) ? response.vectors.map(&:size) : response.vectors.size
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
module Image
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class << self
|
|
8
|
+
alias_method :original_paint, :paint
|
|
9
|
+
def paint(prompt, model: nil, provider: nil, assume_model_exists: false, size: "1024x1024", context: nil)
|
|
10
|
+
raw_payload = {
|
|
11
|
+
provider:,
|
|
12
|
+
size:
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
ActiveSupport::Notifications.instrument("paint_image.ruby_llm", raw_payload) do |payload|
|
|
16
|
+
original_paint(prompt, model:, provider:, assume_model_exists:, size:, context:).tap do |response|
|
|
17
|
+
payload[:image] = response
|
|
18
|
+
payload[:model] = response.model
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
module Moderation
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class << self
|
|
8
|
+
alias_method :original_moderate, :moderate
|
|
9
|
+
def moderate(input, model: nil, provider: nil, assume_model_exists: false, context: nil)
|
|
10
|
+
raw_payload = {
|
|
11
|
+
provider:
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
ActiveSupport::Notifications.instrument("moderate_text.ruby_llm", raw_payload) do |payload|
|
|
15
|
+
original_moderate(input, model:, provider:, assume_model_exists:, context:).tap do |response|
|
|
16
|
+
payload[:moderation] = response
|
|
17
|
+
payload[:model] = response.model
|
|
18
|
+
payload[:flagged] = response.flagged?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
class Railtie < ::Rails::Railtie
|
|
4
|
+
INFLECTION_OVERRIDES = { "ruby_llm" => "RubyLLM" }.freeze
|
|
5
|
+
|
|
6
|
+
initializer "ruby_llm_instrumentation.inflector", after: "ruby_llm.inflections", before: :set_autoload_paths do
|
|
7
|
+
ActiveSupport::Inflector.inflections(:en) do |inflections|
|
|
8
|
+
# The RubyLLM gem registers "RubyLLM" as an acronym in its railtie,
|
|
9
|
+
# which breaks underscore conversion (RubyLLM.underscore => "rubyllm").
|
|
10
|
+
# We need to remove it and use "LLM" as an acronym instead for proper conversion:
|
|
11
|
+
# * "ruby_llm".camelize => "RubyLLM" (not "RubyLlm")
|
|
12
|
+
# * "RubyLLM".underscore => "ruby_llm" (not "rubyllm")
|
|
13
|
+
inflections.acronyms.delete("rubyllm")
|
|
14
|
+
inflections.acronym("LLM")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Rails.autoloaders.each do |loader|
|
|
18
|
+
loader.inflector.inflect(INFLECTION_OVERRIDES)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module RubyLLM
|
|
2
|
+
module Instrumentation
|
|
3
|
+
module Transcription
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class << self
|
|
8
|
+
alias_method :original_transcribe, :transcribe
|
|
9
|
+
def transcribe(audio_file, **kwargs)
|
|
10
|
+
raw_payload = {
|
|
11
|
+
provider: kwargs[:provider]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
ActiveSupport::Notifications.instrument("transcribe_audio.ruby_llm", raw_payload) do |payload|
|
|
15
|
+
original_transcribe(audio_file, **kwargs).tap do |response|
|
|
16
|
+
payload[:transcription] = response
|
|
17
|
+
payload[:model] = response.model
|
|
18
|
+
%i[input_tokens output_tokens duration].each do |field|
|
|
19
|
+
value = response.public_send(field)
|
|
20
|
+
payload[field] = value unless value.nil?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "ruby_llm"
|
|
2
|
+
require "ruby_llm/instrumentation/version"
|
|
3
|
+
require "ruby_llm/instrumentation/railtie"
|
|
4
|
+
|
|
5
|
+
module RubyLLM
|
|
6
|
+
module Instrumentation
|
|
7
|
+
autoload :Chat, "ruby_llm/instrumentation/chat"
|
|
8
|
+
autoload :Embedding, "ruby_llm/instrumentation/embedding"
|
|
9
|
+
autoload :Image, "ruby_llm/instrumentation/image"
|
|
10
|
+
autoload :Transcription, "ruby_llm/instrumentation/transcription"
|
|
11
|
+
autoload :Moderation, "ruby_llm/instrumentation/moderation"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RubyLLM::Chat.include RubyLLM::Instrumentation::Chat
|
|
16
|
+
RubyLLM::Embedding.include RubyLLM::Instrumentation::Embedding
|
|
17
|
+
RubyLLM::Image.include RubyLLM::Instrumentation::Image
|
|
18
|
+
RubyLLM::Transcription.include RubyLLM::Instrumentation::Transcription
|
|
19
|
+
RubyLLM::Moderation.include RubyLLM::Instrumentation::Moderation
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby_llm-instrumentation
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Patricio Mac Adden
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 7.0.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 7.0.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ruby_llm
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
description: Rails instrumentation for RubyLLM
|
|
41
|
+
email:
|
|
42
|
+
- patricio.macadden@sinaptia.dev
|
|
43
|
+
executables: []
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- README.md
|
|
48
|
+
- Rakefile
|
|
49
|
+
- lib/ruby_llm/instrumentation.rb
|
|
50
|
+
- lib/ruby_llm/instrumentation/chat.rb
|
|
51
|
+
- lib/ruby_llm/instrumentation/embedding.rb
|
|
52
|
+
- lib/ruby_llm/instrumentation/image.rb
|
|
53
|
+
- lib/ruby_llm/instrumentation/moderation.rb
|
|
54
|
+
- lib/ruby_llm/instrumentation/railtie.rb
|
|
55
|
+
- lib/ruby_llm/instrumentation/transcription.rb
|
|
56
|
+
- lib/ruby_llm/instrumentation/version.rb
|
|
57
|
+
- lib/tasks/ruby_llm/instrumentation_tasks.rake
|
|
58
|
+
homepage: https://github.com/sinaptia/ruby_llm-instrumentation
|
|
59
|
+
licenses: []
|
|
60
|
+
metadata:
|
|
61
|
+
homepage_uri: https://github.com/sinaptia/ruby_llm-instrumentation
|
|
62
|
+
source_code_uri: https://github.com/sinaptia/ruby_llm-instrumentation
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.6.9
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Rails instrumentation for RubyLLM
|
|
80
|
+
test_files: []
|