opentelemetry-instrumentation-ruby_llm 0.6.0 → 0.7.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 +4 -4
- data/.github/workflows/main.yml +1 -0
- data/Appraisals +4 -0
- data/Gemfile +2 -0
- data/README.md +92 -1
- data/gemfiles/ruby_llm_1.12.1.gemfile +18 -0
- data/gemfiles/ruby_llm_1.8.0.gemfile +2 -0
- data/gemfiles/ruby_llm_1_latest.gemfile +2 -0
- data/lib/opentelemetry/instrumentation/ruby_llm/instrumentation.rb +26 -0
- data/lib/opentelemetry/instrumentation/ruby_llm/message_formatter.rb +97 -0
- data/lib/opentelemetry/instrumentation/ruby_llm/patches/agent.rb +84 -0
- data/lib/opentelemetry/instrumentation/ruby_llm/patches/chat.rb +15 -28
- data/lib/opentelemetry/instrumentation/ruby_llm/patches/chat_methods.rb +17 -0
- data/lib/opentelemetry/instrumentation/ruby_llm/version.rb +1 -1
- data/test/active_record_chat_methods_test.rb +195 -0
- data/test/agent_instrumentation_test.rb +245 -0
- data/test/instrumentation_test.rb +194 -236
- data/test/test_helper.rb +30 -0
- metadata +7 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require "active_record"
|
|
2
|
+
require_relative "test_helper"
|
|
3
|
+
|
|
4
|
+
gem_dir = Gem.loaded_specs.fetch("ruby_llm").full_gem_path
|
|
5
|
+
Dir[File.join(gem_dir, "lib/ruby_llm/active_record/*.rb")].sort.each { |file| require file }
|
|
6
|
+
|
|
7
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
|
8
|
+
ActiveRecord::Base.include RubyLLM::ActiveRecord::ActsAs
|
|
9
|
+
|
|
10
|
+
RubyLLM::Models.class_eval do
|
|
11
|
+
if method_defined?(:load_models)
|
|
12
|
+
def load_models
|
|
13
|
+
read_from_json
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
def self.load_models(file = RubyLLM.config.model_registry_file)
|
|
17
|
+
read_from_json(file)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
ActiveRecord::Schema.verbose = false
|
|
23
|
+
ActiveRecord::Schema.define do
|
|
24
|
+
create_table :models do |t|
|
|
25
|
+
t.string :model_id, null: false
|
|
26
|
+
t.string :name, null: false
|
|
27
|
+
t.string :provider, null: false
|
|
28
|
+
t.string :family
|
|
29
|
+
t.datetime :model_created_at
|
|
30
|
+
t.integer :context_window
|
|
31
|
+
t.integer :max_output_tokens
|
|
32
|
+
t.date :knowledge_cutoff
|
|
33
|
+
t.json :modalities, default: {}
|
|
34
|
+
t.json :capabilities, default: []
|
|
35
|
+
t.json :pricing, default: {}
|
|
36
|
+
t.json :metadata, default: {}
|
|
37
|
+
t.timestamps
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
create_table :chats do |t|
|
|
41
|
+
t.references :model
|
|
42
|
+
t.timestamps
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
create_table :messages do |t|
|
|
46
|
+
t.string :role, null: false
|
|
47
|
+
t.text :content
|
|
48
|
+
t.json :content_raw
|
|
49
|
+
t.text :thinking_text
|
|
50
|
+
t.text :thinking_signature
|
|
51
|
+
t.integer :thinking_tokens
|
|
52
|
+
t.integer :input_tokens
|
|
53
|
+
t.integer :output_tokens
|
|
54
|
+
t.integer :cached_tokens
|
|
55
|
+
t.integer :cache_creation_tokens
|
|
56
|
+
t.references :chat, null: false
|
|
57
|
+
t.references :model
|
|
58
|
+
t.references :tool_call
|
|
59
|
+
t.timestamps
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
create_table :tool_calls do |t|
|
|
63
|
+
t.string :tool_call_id, null: false
|
|
64
|
+
t.string :name, null: false
|
|
65
|
+
t.text :thought_signature
|
|
66
|
+
t.json :arguments, default: {}
|
|
67
|
+
t.references :message, null: false
|
|
68
|
+
t.timestamps
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class Model < ActiveRecord::Base
|
|
73
|
+
acts_as_model
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class Chat < ActiveRecord::Base
|
|
77
|
+
acts_as_chat
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class Message < ActiveRecord::Base
|
|
81
|
+
acts_as_message
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class ToolCall < ActiveRecord::Base
|
|
85
|
+
acts_as_tool_call
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if defined?(RubyLLM::Agent)
|
|
89
|
+
class SupportAgent < RubyLLM::Agent
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class ActiveRecordChatMethodsTest < Minitest::Test
|
|
94
|
+
include ChatCompletionStubs
|
|
95
|
+
|
|
96
|
+
def setup
|
|
97
|
+
EXPORTER.reset
|
|
98
|
+
|
|
99
|
+
RubyLLM.configure do |c|
|
|
100
|
+
c.openai_api_key = "fake-key-for-testing"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def find_or_create_model
|
|
105
|
+
Model.find_or_create_by!(model_id: "gpt-4o-mini", provider: "openai") do |m|
|
|
106
|
+
m.name = "GPT-4o Mini"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def create_chat_record
|
|
111
|
+
Chat.create!(model: find_or_create_model)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def find_chat_span
|
|
115
|
+
EXPORTER.finished_spans.find { |s| s.name.start_with?("chat ") }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def find_agent_span
|
|
119
|
+
EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def test_persisted_record_ask_stamps_conversation_id_on_chat_span
|
|
123
|
+
stub_chat_completion
|
|
124
|
+
|
|
125
|
+
chat_record = create_chat_record
|
|
126
|
+
chat_record.ask("Hi")
|
|
127
|
+
|
|
128
|
+
assert_equal chat_record.id.to_s, find_chat_span.attributes["gen_ai.conversation.id"]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_plain_chat_span_carries_no_conversation_id
|
|
132
|
+
stub_chat_completion
|
|
133
|
+
|
|
134
|
+
RubyLLM.chat(model: "gpt-4o-mini").ask("Hi")
|
|
135
|
+
|
|
136
|
+
assert_nil find_chat_span.attributes["gen_ai.conversation.id"]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_unpersisted_record_chat_is_not_stamped_until_persisted
|
|
140
|
+
chat_record = Chat.new(model: find_or_create_model)
|
|
141
|
+
|
|
142
|
+
assert_nil chat_record.to_llm.otel_conversation_id
|
|
143
|
+
|
|
144
|
+
chat_record.save!
|
|
145
|
+
|
|
146
|
+
assert_equal chat_record.id.to_s, chat_record.to_llm.otel_conversation_id
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_user_otel_attributes_are_preserved_alongside_conversation_id
|
|
150
|
+
stub_chat_completion
|
|
151
|
+
|
|
152
|
+
chat_record = create_chat_record
|
|
153
|
+
chat_record.to_llm.with_otel_attributes("enduser.id" => "user-1")
|
|
154
|
+
chat_record.ask("Hi")
|
|
155
|
+
|
|
156
|
+
span = find_chat_span
|
|
157
|
+
assert_equal "user-1", span.attributes["enduser.id"]
|
|
158
|
+
assert_equal chat_record.id.to_s, span.attributes["gen_ai.conversation.id"]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_user_supplied_conversation_id_wins_over_record_id
|
|
162
|
+
stub_chat_completion
|
|
163
|
+
|
|
164
|
+
chat_record = create_chat_record
|
|
165
|
+
chat_record.to_llm.with_otel_attributes("gen_ai.conversation.id" => "custom-id")
|
|
166
|
+
chat_record.ask("Hi")
|
|
167
|
+
|
|
168
|
+
assert_equal "custom-id", find_chat_span.attributes["gen_ai.conversation.id"]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if defined?(RubyLLM::Agent)
|
|
172
|
+
def test_agent_flow_carries_conversation_id_on_root_and_chat_spans
|
|
173
|
+
stub_chat_completion
|
|
174
|
+
|
|
175
|
+
chat_record = create_chat_record
|
|
176
|
+
agent = SupportAgent.new(chat: chat_record)
|
|
177
|
+
agent.ask("Hi")
|
|
178
|
+
|
|
179
|
+
assert_equal chat_record.id.to_s, find_agent_span.attributes["gen_ai.conversation.id"]
|
|
180
|
+
assert_equal chat_record.id.to_s, find_chat_span.attributes["gen_ai.conversation.id"]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_user_supplied_conversation_id_wins_on_agent_and_chat_spans
|
|
184
|
+
stub_chat_completion
|
|
185
|
+
|
|
186
|
+
chat_record = create_chat_record
|
|
187
|
+
chat_record.to_llm.with_otel_attributes("gen_ai.conversation.id" => "custom-id")
|
|
188
|
+
agent = SupportAgent.new(chat: chat_record)
|
|
189
|
+
agent.ask("Hi")
|
|
190
|
+
|
|
191
|
+
assert_equal "custom-id", find_agent_span.attributes["gen_ai.conversation.id"]
|
|
192
|
+
assert_equal "custom-id", find_chat_span.attributes["gen_ai.conversation.id"]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
if defined?(RubyLLM::Agent)
|
|
4
|
+
class ResearchAgent < RubyLLM::Agent
|
|
5
|
+
model "gpt-4o-mini"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class AgentInstrumentationTest < Minitest::Test
|
|
9
|
+
include ChatCompletionStubs
|
|
10
|
+
|
|
11
|
+
def setup
|
|
12
|
+
EXPORTER.reset
|
|
13
|
+
|
|
14
|
+
RubyLLM.configure do |c|
|
|
15
|
+
c.openai_api_key = "fake-key-for-testing"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_wraps_ask_in_invoke_agent_span_with_chat_span_as_child
|
|
20
|
+
stub_chat_completion
|
|
21
|
+
|
|
22
|
+
agent = ResearchAgent.new
|
|
23
|
+
agent.ask("Hi")
|
|
24
|
+
|
|
25
|
+
spans = EXPORTER.finished_spans
|
|
26
|
+
agent_span = spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
27
|
+
chat_span = spans.find { |s| s.name.start_with?("chat ") }
|
|
28
|
+
|
|
29
|
+
assert_equal "invoke_agent ResearchAgent", agent_span.name
|
|
30
|
+
assert_equal OpenTelemetry::Trace::SpanKind::INTERNAL, agent_span.kind
|
|
31
|
+
assert_equal "invoke_agent", agent_span.attributes["gen_ai.operation.name"]
|
|
32
|
+
assert_equal "ResearchAgent", agent_span.attributes["gen_ai.agent.name"]
|
|
33
|
+
assert_equal agent_span.span_id, chat_span.parent_span_id
|
|
34
|
+
assert_equal agent_span.trace_id, chat_span.trace_id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_wraps_complete_called_directly_in_invoke_agent_span
|
|
38
|
+
stub_chat_completion
|
|
39
|
+
|
|
40
|
+
agent = ResearchAgent.new
|
|
41
|
+
agent.add_message(role: :user, content: "Hi")
|
|
42
|
+
agent.complete
|
|
43
|
+
|
|
44
|
+
spans = EXPORTER.finished_spans
|
|
45
|
+
agent_span = spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
46
|
+
chat_span = spans.find { |s| s.name.start_with?("chat ") }
|
|
47
|
+
|
|
48
|
+
assert_equal "invoke_agent ResearchAgent", agent_span.name
|
|
49
|
+
assert_equal agent_span.span_id, chat_span.parent_span_id
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_omits_agent_name_for_anonymous_agent_classes
|
|
53
|
+
stub_chat_completion
|
|
54
|
+
|
|
55
|
+
agent_class = Class.new(RubyLLM::Agent) do
|
|
56
|
+
model "gpt-4o-mini"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
agent_class.new.ask("Hi")
|
|
60
|
+
|
|
61
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
62
|
+
|
|
63
|
+
assert_equal "invoke_agent", agent_span.name
|
|
64
|
+
assert_nil agent_span.attributes["gen_ai.agent.name"]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_sets_conversation_id_stamped_on_the_chat
|
|
68
|
+
stub_chat_completion
|
|
69
|
+
|
|
70
|
+
chat = RubyLLM.chat(model: "gpt-4o-mini")
|
|
71
|
+
chat.otel_conversation_id = "42"
|
|
72
|
+
|
|
73
|
+
agent = ResearchAgent.new(chat: chat)
|
|
74
|
+
agent.ask("Hi")
|
|
75
|
+
|
|
76
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
77
|
+
assert_equal "42", agent_span.attributes["gen_ai.conversation.id"]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_omits_conversation_id_for_unpersisted_chats
|
|
81
|
+
stub_chat_completion
|
|
82
|
+
|
|
83
|
+
ResearchAgent.new.ask("Hi")
|
|
84
|
+
|
|
85
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
86
|
+
assert_nil agent_span.attributes["gen_ai.conversation.id"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_records_error_on_agent_span_when_ask_fails
|
|
90
|
+
stub_request(:post, "https://api.openai.com/v1/chat/completions")
|
|
91
|
+
.to_return(status: 500, body: "Internal Server Error")
|
|
92
|
+
|
|
93
|
+
agent = ResearchAgent.new
|
|
94
|
+
|
|
95
|
+
assert_raises do
|
|
96
|
+
agent.ask("Hi")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
100
|
+
assert agent_span.attributes["error.type"]
|
|
101
|
+
assert_equal OpenTelemetry::Trace::Status::ERROR, agent_span.status.code
|
|
102
|
+
|
|
103
|
+
exception_events = agent_span.events.select { |e| e.name == "exception" }
|
|
104
|
+
assert_equal 1, exception_events.length
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_sets_otel_attributes_on_agent_span_when_ask_fails
|
|
108
|
+
stub_request(:post, "https://api.openai.com/v1/chat/completions")
|
|
109
|
+
.to_return(status: 500, body: "Internal Server Error")
|
|
110
|
+
|
|
111
|
+
agent = ResearchAgent.new
|
|
112
|
+
agent.with_otel_attributes("langfuse.session.id" => "session-1")
|
|
113
|
+
|
|
114
|
+
assert_raises do
|
|
115
|
+
agent.ask("Hi")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
119
|
+
assert_equal "session-1", agent_span.attributes["langfuse.session.id"]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def test_with_otel_attributes_sets_attributes_on_agent_and_chat_spans
|
|
123
|
+
stub_chat_completion
|
|
124
|
+
|
|
125
|
+
agent = ResearchAgent.new
|
|
126
|
+
result = agent.with_otel_attributes("langfuse.session.id" => "session-1")
|
|
127
|
+
agent.ask("Hi")
|
|
128
|
+
|
|
129
|
+
assert_same agent, result
|
|
130
|
+
|
|
131
|
+
spans = EXPORTER.finished_spans
|
|
132
|
+
agent_span = spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
133
|
+
chat_span = spans.find { |s| s.name.start_with?("chat ") }
|
|
134
|
+
|
|
135
|
+
assert_equal "session-1", agent_span.attributes["langfuse.session.id"]
|
|
136
|
+
assert_equal "session-1", chat_span.attributes["langfuse.session.id"]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_with_otel_attributes_forwards_through_to_llm_for_chat_records
|
|
140
|
+
stub_chat_completion
|
|
141
|
+
|
|
142
|
+
llm_chat = RubyLLM.chat(model: "gpt-4o-mini")
|
|
143
|
+
llm_chat.otel_conversation_id = "7"
|
|
144
|
+
record = Object.new
|
|
145
|
+
record.define_singleton_method(:to_llm) { llm_chat }
|
|
146
|
+
record.define_singleton_method(:ask) { |message| llm_chat.ask(message) }
|
|
147
|
+
|
|
148
|
+
agent = ResearchAgent.new(chat: record)
|
|
149
|
+
agent.with_otel_attributes("user.id" => "u1")
|
|
150
|
+
agent.ask("Hi")
|
|
151
|
+
|
|
152
|
+
spans = EXPORTER.finished_spans
|
|
153
|
+
agent_span = spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
154
|
+
chat_span = spans.find { |s| s.name.start_with?("chat ") }
|
|
155
|
+
|
|
156
|
+
assert_equal "7", agent_span.attributes["gen_ai.conversation.id"]
|
|
157
|
+
assert_equal "u1", agent_span.attributes["user.id"]
|
|
158
|
+
assert_equal "u1", chat_span.attributes["user.id"]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_wraps_say_in_invoke_agent_span
|
|
162
|
+
stub_chat_completion
|
|
163
|
+
|
|
164
|
+
ResearchAgent.new.say("Hi")
|
|
165
|
+
|
|
166
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
167
|
+
assert_equal "invoke_agent ResearchAgent", agent_span.name
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_creates_one_agent_span_for_ask_with_tool_loop
|
|
171
|
+
calculator = Class.new(RubyLLM::Tool) do
|
|
172
|
+
def self.name = "calculator"
|
|
173
|
+
description "Performs math"
|
|
174
|
+
param :expression, type: "string", desc: "Math expression"
|
|
175
|
+
|
|
176
|
+
def execute(expression:)
|
|
177
|
+
eval(expression).to_s
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
stub_chat_completion(
|
|
182
|
+
chat_completion_body(
|
|
183
|
+
content: nil,
|
|
184
|
+
tool_calls: [{
|
|
185
|
+
id: "call_abc123",
|
|
186
|
+
type: "function",
|
|
187
|
+
function: { name: "calculator", arguments: '{"expression":"2+2"}' }
|
|
188
|
+
}]
|
|
189
|
+
),
|
|
190
|
+
chat_completion_body(
|
|
191
|
+
content: "The answer is 4",
|
|
192
|
+
usage: { prompt_tokens: 20, completion_tokens: 5, total_tokens: 25 }
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
agent = ResearchAgent.new
|
|
197
|
+
agent.with_tool(calculator)
|
|
198
|
+
agent.ask("What is 2+2?")
|
|
199
|
+
|
|
200
|
+
spans = EXPORTER.finished_spans
|
|
201
|
+
agent_spans = spans.select { |s| s.name.start_with?("invoke_agent") }
|
|
202
|
+
chat_spans = spans.select { |s| s.name.start_with?("chat ") }
|
|
203
|
+
tool_spans = spans.select { |s| s.name.start_with?("execute_tool ") }
|
|
204
|
+
|
|
205
|
+
assert_equal 1, agent_spans.length
|
|
206
|
+
assert_equal 2, chat_spans.length
|
|
207
|
+
assert_equal 1, tool_spans.length
|
|
208
|
+
|
|
209
|
+
agent_span = agent_spans.first
|
|
210
|
+
assert(chat_spans.all? { |s| s.trace_id == agent_span.trace_id })
|
|
211
|
+
assert_equal agent_span.trace_id, tool_spans.first.trace_id
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def test_captures_input_and_output_on_agent_span_when_enabled
|
|
215
|
+
OpenTelemetry::Instrumentation::RubyLLM::Instrumentation.instance.config[:capture_content] = true
|
|
216
|
+
stub_chat_completion
|
|
217
|
+
|
|
218
|
+
ResearchAgent.new.ask("Hi")
|
|
219
|
+
|
|
220
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
221
|
+
|
|
222
|
+
input_messages = JSON.parse(agent_span.attributes["gen_ai.input.messages"])
|
|
223
|
+
assert_equal 1, input_messages.length
|
|
224
|
+
assert_equal "user", input_messages[0]["role"]
|
|
225
|
+
assert_equal [{ "type" => "text", "content" => "Hi" }], input_messages[0]["parts"]
|
|
226
|
+
|
|
227
|
+
output_messages = JSON.parse(agent_span.attributes["gen_ai.output.messages"])
|
|
228
|
+
assert_equal 1, output_messages.length
|
|
229
|
+
assert_equal "assistant", output_messages[0]["role"]
|
|
230
|
+
assert_equal [{ "type" => "text", "content" => "Hello, world!" }], output_messages[0]["parts"]
|
|
231
|
+
ensure
|
|
232
|
+
OpenTelemetry::Instrumentation::RubyLLM::Instrumentation.instance.config[:capture_content] = false
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def test_does_not_capture_content_on_agent_span_by_default
|
|
236
|
+
stub_chat_completion
|
|
237
|
+
|
|
238
|
+
ResearchAgent.new.ask("Hi")
|
|
239
|
+
|
|
240
|
+
agent_span = EXPORTER.finished_spans.find { |s| s.name.start_with?("invoke_agent") }
|
|
241
|
+
assert_nil agent_span.attributes["gen_ai.input.messages"]
|
|
242
|
+
assert_nil agent_span.attributes["gen_ai.output.messages"]
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|