Skip to content

Remove outer rescue blocks that caused duplicate RubyLLM calls#20

Closed
ebeigarts wants to merge 2 commits into
thoughtbot:mainfrom
mitigate-dev:feature/avoid-duplicate-rubyllm-calls
Closed

Remove outer rescue blocks that caused duplicate RubyLLM calls#20
ebeigarts wants to merge 2 commits into
thoughtbot:mainfrom
mitigate-dev:feature/avoid-duplicate-rubyllm-calls

Conversation

@ebeigarts
Copy link
Copy Markdown
Contributor

The outer rescue StandardError blocks in complete, execute_tool,
and embed were catching errors from instrumentation, logging them
via OpenTelemetry.handle_error, and then calling super — which
re-executed the original RubyLLM method, resulting in duplicate API
calls to the LLM provider.

This is safe to remove because Tracer#in_span already handles
exceptions internally by recording them and re-raising:
https://github.com/open-telemetry/opentelemetry-ruby/blob/0b94ef6086facf3c7ad584485bb3825b0ab90e39/api/lib/opentelemetry/trace/tracer.rb#L34-L44

This pattern is also not used in other opentelemetry-ruby-contrib
instrumentation libraries:
https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-ruby-contrib+handle_error&type=code

The inner begin/rescue blocks inside each span are preserved as they
add the error.type attribute per GenAI semantic conventions.

ebeigarts and others added 2 commits April 10, 2026 18:19
Instrument the embeddings endpoint following the OpenTelemetry GenAI
semantic conventions for embedding spans. The patch is applied via
singleton_class.prepend on RubyLLM::Embedding, wrapping the class-level
embed method with a CLIENT span.

Span attributes:
- gen_ai.operation.name: "embeddings"
- gen_ai.provider.name: resolved provider (e.g. "openai")
- gen_ai.request.model / gen_ai.response.model: model identifiers
- gen_ai.usage.input_tokens: token count from the response
- gen_ai.embeddings.dimension.count: length of the embedding vector
- error.type: exception class on API failures

Error handling follows the same pattern as Chat instrumentation:
API errors are recorded on the span and re-raised, while
instrumentation failures are swallowed to avoid breaking the
underlying embed call.

Tests cover span creation with all attributes, error recording on
API failure, and resilience when instrumentation itself fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The outer `rescue StandardError` blocks in `complete`, `execute_tool`,
and `embed` were catching errors from instrumentation, logging them
via `OpenTelemetry.handle_error`, and then calling `super` — which
re-executed the original RubyLLM method, resulting in duplicate API
calls to the LLM provider.

This is safe to remove because `Tracer#in_span` already handles
exceptions internally by recording them and re-raising:
https://github.com/open-telemetry/opentelemetry-ruby/blob/0b94ef6086facf3c7ad584485bb3825b0ab90e39/api/lib/opentelemetry/trace/tracer.rb#L34-L44

This pattern is also not used in other opentelemetry-ruby-contrib
instrumentation libraries:
https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-ruby-contrib+handle_error&type=code

The inner `begin/rescue` blocks inside each span are preserved as they
add the `error.type` attribute per GenAI semantic conventions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ebeigarts
Copy link
Copy Markdown
Contributor Author

ebeigarts commented Apr 10, 2026

I will rebase after #17 gets merged or you can just cherry-pick 8da3544

@clarissalimab
Copy link
Copy Markdown
Contributor

I cherry picked this into main, so I'm closing. Thank you for catching this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants