From 14bb1e6629ea766538f42898844c7d705899b190 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Tue, 11 Nov 2025 15:05:34 -0500 Subject: [PATCH 1/6] adding gen_ai --- .../lib/opentelemetry/semantic_conventions.rb | 9 +++ traceloop-sdk/lib/traceloop/sdk.rb | 58 +++++++++++++++---- traceloop-sdk/traceloop-sdk.gemspec | 4 +- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb index 891eaf9..c482232 100644 --- a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb +++ b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb @@ -30,6 +30,15 @@ module SpanAttributes # Deprecated TRACELOOP_CORRELATION_ID = "traceloop.correlation.id" + + # Gen AI + GEN_AI_REQUEST_MODEL = "gen_ai.request.model" + GEN_AI_RESPONSE_MODEL = "gen_ai.response.model" + GEN_AI_USAGE_COMPLETION_TOKENS = "gen_ai.usage.completion_tokens" + GEN_AI_USAGE_PROMPT_TOKENS = "gen_ai.usage.prompt_tokens" + GEN_AI_COMPLETIONS = "gen_ai.completion" + GEN_AI_PROMPTS = "gen_ai.prompt" + GEN_AI_SYSTEM = "gen_ai.system" end module LLMRequestTypeValues diff --git a/traceloop-sdk/lib/traceloop/sdk.rb b/traceloop-sdk/lib/traceloop/sdk.rb index 640be53..a9e5f9f 100644 --- a/traceloop-sdk/lib/traceloop/sdk.rb +++ b/traceloop-sdk/lib/traceloop/sdk.rb @@ -11,11 +11,13 @@ def initialize OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new( OpenTelemetry::Exporter::OTLP::Exporter.new( endpoint: "#{ENV.fetch("TRACELOOP_BASE_URL", "https://api.traceloop.com")}/v1/traces", - headers: { "Authorization" => "Bearer #{ENV.fetch("TRACELOOP_API_KEY")}" } + headers: { + Authorization: "#{ENV.fetch("TRACELOOP_AUTH_SCHEME", "Bearer")} #{ENV.fetch("TRACELOOP_API_KEY")}" + } ) ) ) - puts "Traceloop exporting traces to #{ENV.fetch("TRACELOOP_BASE", "https://api.traceloop.com")}" + puts "Traceloop exporting traces to #{ENV.fetch("TRACELOOP_BASE_URL", "https://api.traceloop.com")}" end @tracer = OpenTelemetry.tracer_provider.tracer("Traceloop") @@ -41,15 +43,15 @@ def log_messages(messages) def log_prompt(system_prompt="", user_prompt) unless system_prompt.empty? @span.add_attributes({ - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.0.role" => "system", - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.0.content" => system_prompt, - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.1.role" => "user", - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.1.content" => user_prompt + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.0.role" => "system", + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.0.content" => system_prompt, + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.1.role" => "user", + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.1.content" => user_prompt }) else @span.add_attributes({ - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.0.role" => "user", - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_PROMPTS}.0.content" => user_prompt + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.0.role" => "user", + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROMPTS}.0.content" => user_prompt }) end end @@ -57,9 +59,12 @@ def log_prompt(system_prompt="", user_prompt) def log_response(response) if response.respond_to?(:body) log_bedrock_response(response) + # Check for RubyLLM::Message objects + elsif response.instance_of?(::RubyLLM::Message) + log_ruby_llm_response(response) # This is Gemini specific, see - # https://github.com/gbaptista/gemini-ai?tab=readme-ov-file#generate_content - elsif response.has_key?("candidates") + elsif response.respond_to?(:has_key?) && response.has_key?("candidates") log_gemini_response(response) else log_openai_response(response) @@ -77,6 +82,38 @@ def log_gemini_response(response) }) end + def log_ruby_llm_response(response) + model = response.respond_to?(:model_id) ? response.model_id : @model + @span.add_attributes({ + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => model, + }) + + if response.respond_to?(:input_tokens) && response.input_tokens && + response.respond_to?(:output_tokens) && response.output_tokens + @span.add_attributes({ + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens, + }) + end + + if response.respond_to?(:content) && response.content + content_text = "" + role = response.respond_to?(:role) ? response.role.to_s : "assistant" + + # Handle RubyLLM::Content object + if response.content.respond_to?(:text) + content_text = response.content.text + elsif response.content.respond_to?(:to_s) + content_text = response.content.to_s + end + + @span.add_attributes({ + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => role, + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => content_text + }) + end + end + def log_bedrock_response(response) body = JSON.parse(response.body.read()) @@ -126,7 +163,8 @@ def log_openai_response(response) def llm_call(provider, model) @tracer.in_span("#{provider}.chat") do |span| span.add_attributes({ - OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_REQUEST_MODEL => model, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_REQUEST_MODEL => model, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_SYSTEM => provider }) yield Tracer.new(span, provider, model) end diff --git a/traceloop-sdk/traceloop-sdk.gemspec b/traceloop-sdk/traceloop-sdk.gemspec index b985c60..9acbe62 100644 --- a/traceloop-sdk/traceloop-sdk.gemspec +++ b/traceloop-sdk/traceloop-sdk.gemspec @@ -17,8 +17,8 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-semantic_conventions_ai', '~> 0.0.3' - spec.add_dependency 'opentelemetry-sdk', '~> 1.3.1' - spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.26.1' + spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.31.1' + spec.add_dependency 'opentelemetry-sdk', '~> 1.10.0' if spec.respond_to?(:metadata) spec.metadata['source_code_uri'] = 'https://github.com/traceloop/openllmetry-ruby/tree/main/traceloop-sdk' From 987d252d4a80c86750526e575604fee010711716 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Wed, 12 Nov 2025 16:56:23 -0500 Subject: [PATCH 2/6] adding halting --- traceloop-sdk/lib/traceloop/sdk.rb | 46 +++++++++++------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/traceloop-sdk/lib/traceloop/sdk.rb b/traceloop-sdk/lib/traceloop/sdk.rb index a9e5f9f..932bb4d 100644 --- a/traceloop-sdk/lib/traceloop/sdk.rb +++ b/traceloop-sdk/lib/traceloop/sdk.rb @@ -8,7 +8,7 @@ class Traceloop def initialize OpenTelemetry::SDK.configure do |c| c.add_span_processor( - OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new( + OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new( OpenTelemetry::Exporter::OTLP::Exporter.new( endpoint: "#{ENV.fetch("TRACELOOP_BASE_URL", "https://api.traceloop.com")}/v1/traces", headers: { @@ -61,7 +61,9 @@ def log_response(response) log_bedrock_response(response) # Check for RubyLLM::Message objects elsif response.instance_of?(::RubyLLM::Message) - log_ruby_llm_response(response) + log_ruby_llm_message(response) + elsif response.instance_of?(::RubyLLM::Tool::Halt) + log_ruby_llm_halt(response) # This is Gemini specific, see - # https://github.com/gbaptista/gemini-ai?tab=readme-ov-file#generate_content elsif response.respond_to?(:has_key?) && response.has_key?("candidates") @@ -82,36 +84,22 @@ def log_gemini_response(response) }) end - def log_ruby_llm_response(response) - model = response.respond_to?(:model_id) ? response.model_id : @model + def log_ruby_llm_message(response) @span.add_attributes({ - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => model, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => response.model_id, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens, + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => response.role.to_s, + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => response.content }) + end - if response.respond_to?(:input_tokens) && response.input_tokens && - response.respond_to?(:output_tokens) && response.output_tokens - @span.add_attributes({ - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens, - }) - end - - if response.respond_to?(:content) && response.content - content_text = "" - role = response.respond_to?(:role) ? response.role.to_s : "assistant" - - # Handle RubyLLM::Content object - if response.content.respond_to?(:text) - content_text = response.content.text - elsif response.content.respond_to?(:to_s) - content_text = response.content.to_s - end - - @span.add_attributes({ - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => role, - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => content_text - }) - end + def log_ruby_llm_halt(response) + @span.add_attributes({ + OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_RESPONSE_MODEL => @model, + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => "tool", + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => response.content + }) end def log_bedrock_response(response) From 6b2bc5f13db534a94892c611aae61dd6563496e2 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Fri, 14 Nov 2025 14:01:56 -0500 Subject: [PATCH 3/6] coderabbit comments --- traceloop-sdk/lib/traceloop/sdk.rb | 33 +++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/traceloop-sdk/lib/traceloop/sdk.rb b/traceloop-sdk/lib/traceloop/sdk.rb index 932bb4d..ab3d941 100644 --- a/traceloop-sdk/lib/traceloop/sdk.rb +++ b/traceloop-sdk/lib/traceloop/sdk.rb @@ -6,13 +6,16 @@ module Traceloop module SDK class Traceloop def initialize + api_key = ENV["TRACELOOP_API_KEY"] + raise "TRACELOOP_API_KEY environment variable is required" if api_key.nil? || api_key.empty? + OpenTelemetry::SDK.configure do |c| c.add_span_processor( OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new( OpenTelemetry::Exporter::OTLP::Exporter.new( endpoint: "#{ENV.fetch("TRACELOOP_BASE_URL", "https://api.traceloop.com")}/v1/traces", headers: { - Authorization: "#{ENV.fetch("TRACELOOP_AUTH_SCHEME", "Bearer")} #{ENV.fetch("TRACELOOP_API_KEY")}" + "Authorization" => "#{ENV.fetch("TRACELOOP_AUTH_SCHEME", "Bearer")} #{ENV.fetch("TRACELOOP_API_KEY")}" } ) ) @@ -60,9 +63,9 @@ def log_response(response) if response.respond_to?(:body) log_bedrock_response(response) # Check for RubyLLM::Message objects - elsif response.instance_of?(::RubyLLM::Message) + elsif response.is_a?(::RubyLLM::Message) log_ruby_llm_message(response) - elsif response.instance_of?(::RubyLLM::Tool::Halt) + elsif response.is_a?(::RubyLLM::Tool::Halt) log_ruby_llm_halt(response) # This is Gemini specific, see - # https://github.com/gbaptista/gemini-ai?tab=readme-ov-file#generate_content @@ -80,15 +83,16 @@ def log_gemini_response(response) @span.add_attributes({ "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.role" => "assistant", - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.content" => response.dig("candidates", 0, "content", "parts", 0, "text") + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.content" => response.dig( +"candidates", 0, "content", "parts", 0, "text") }) end def log_ruby_llm_message(response) @span.add_attributes({ OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => response.model_id, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens || 0, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens || 0, "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => response.role.to_s, "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => response.content }) @@ -96,7 +100,7 @@ def log_ruby_llm_message(response) def log_ruby_llm_halt(response) @span.add_attributes({ - OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_RESPONSE_MODEL => @model, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => @model, "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => "tool", "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => response.content }) @@ -134,15 +138,20 @@ def log_openai_response(response) }) if response.has_key?("usage") @span.add_attributes({ - OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_TOTAL_TOKENS => response.dig("usage", "total_tokens"), - OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_COMPLETION_TOKENS => response.dig("usage", "completion_tokens"), - OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_PROMPT_TOKENS => response.dig("usage", "prompt_tokens"), + OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_TOTAL_TOKENS => response.dig("usage", + "total_tokens"), + OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_COMPLETION_TOKENS => response.dig( +"usage", "completion_tokens"), + OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_USAGE_PROMPT_TOKENS => response.dig("usage", + "prompt_tokens"), }) end if response.has_key?("choices") @span.add_attributes({ - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.role" => response.dig("choices", 0, "message", "role"), - "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.content" => response.dig("choices", 0, "message", "content") + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.role" => response.dig( +"choices", 0, "message", "role"), + "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::LLM_COMPLETIONS}.0.content" => response.dig( +"choices", 0, "message", "content") }) end end From 3e0b4c5022331343441c8a7015bf945178744412 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Mon, 17 Nov 2025 16:47:45 -0500 Subject: [PATCH 4/6] adding conversation id --- .../lib/opentelemetry/semantic_conventions.rb | 5 +++-- traceloop-sdk/lib/traceloop/sdk.rb | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb index c482232..4b2b044 100644 --- a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb +++ b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb @@ -34,11 +34,12 @@ module SpanAttributes # Gen AI GEN_AI_REQUEST_MODEL = "gen_ai.request.model" GEN_AI_RESPONSE_MODEL = "gen_ai.response.model" - GEN_AI_USAGE_COMPLETION_TOKENS = "gen_ai.usage.completion_tokens" - GEN_AI_USAGE_PROMPT_TOKENS = "gen_ai.usage.prompt_tokens" + GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens" + GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens" GEN_AI_COMPLETIONS = "gen_ai.completion" GEN_AI_PROMPTS = "gen_ai.prompt" GEN_AI_SYSTEM = "gen_ai.system" + GEN_AI_PROVIDER = "gen_ai.provider.name" end module LLMRequestTypeValues diff --git a/traceloop-sdk/lib/traceloop/sdk.rb b/traceloop-sdk/lib/traceloop/sdk.rb index ab3d941..0088975 100644 --- a/traceloop-sdk/lib/traceloop/sdk.rb +++ b/traceloop-sdk/lib/traceloop/sdk.rb @@ -91,8 +91,8 @@ def log_gemini_response(response) def log_ruby_llm_message(response) @span.add_attributes({ OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_RESPONSE_MODEL => response.model_id, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_COMPLETION_TOKENS => response.output_tokens || 0, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_PROMPT_TOKENS => response.input_tokens || 0, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_OUTPUT_TOKENS => response.output_tokens || 0, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_USAGE_INPUT_TOKENS => response.input_tokens || 0, "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.role" => response.role.to_s, "#{OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_COMPLETIONS}.0.content" => response.content }) @@ -157,12 +157,19 @@ def log_openai_response(response) end end - def llm_call(provider, model) + def llm_call(provider, model, conversation_id: nil) @tracer.in_span("#{provider}.chat") do |span| - span.add_attributes({ + attributes = { OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_REQUEST_MODEL => model, - OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_SYSTEM => provider - }) + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_SYSTEM => provider, + OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_PROVIDER => provider, + } + + if conversation_id + attributes[OpenTelemetry::SemanticConventionsAi::SpanAttributes::GEN_AI_CONVERSATION_ID] = conversation_id + end + + span.add_attributes(attributes) yield Tracer.new(span, provider, model) end end From da3f2b4e23dd41cda00ea92242725996cd20c7e7 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Mon, 17 Nov 2025 16:54:03 -0500 Subject: [PATCH 5/6] adding conversation id -- fix --- .../lib/opentelemetry/semantic_conventions.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb index 4b2b044..b7b55fc 100644 --- a/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb +++ b/semantic_conventions_ai/lib/opentelemetry/semantic_conventions.rb @@ -40,6 +40,7 @@ module SpanAttributes GEN_AI_PROMPTS = "gen_ai.prompt" GEN_AI_SYSTEM = "gen_ai.system" GEN_AI_PROVIDER = "gen_ai.provider.name" + GEN_AI_CONVERSATION_ID = "gen_ai.conversation.id" end module LLMRequestTypeValues From 6509a5df7a62128ea61ee8e396f4653ea734eb01 Mon Sep 17 00:00:00 2001 From: Ania Misiorek Date: Mon, 17 Nov 2025 17:16:57 -0500 Subject: [PATCH 6/6] guarding rubyLLM usage --- traceloop-sdk/lib/traceloop/sdk.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/traceloop-sdk/lib/traceloop/sdk.rb b/traceloop-sdk/lib/traceloop/sdk.rb index 0088975..e5dd2d3 100644 --- a/traceloop-sdk/lib/traceloop/sdk.rb +++ b/traceloop-sdk/lib/traceloop/sdk.rb @@ -63,9 +63,9 @@ def log_response(response) if response.respond_to?(:body) log_bedrock_response(response) # Check for RubyLLM::Message objects - elsif response.is_a?(::RubyLLM::Message) + elsif defined?(::RubyLLM::Message) && response.is_a?(::RubyLLM::Message) log_ruby_llm_message(response) - elsif response.is_a?(::RubyLLM::Tool::Halt) + elsif defined?(::RubyLLM::Tool::Halt) && response.is_a?(::RubyLLM::Tool::Halt) log_ruby_llm_halt(response) # This is Gemini specific, see - # https://github.com/gbaptista/gemini-ai?tab=readme-ov-file#generate_content