Module: Foam::Otel::LLM::RubyOpenAIShim
- Defined in:
- lib/foam/otel/llm/ruby_openai_shim.rb
Overview
Gap-filler for the ruby-openai gem (the long-standing community
OpenAI client: OpenAI::Client#chat, NOT the official openai
gem's resource layer OpenAIShim covers). Plenty of production Rails
apps still ride it — presence-checked and version-windowed like its
siblings so it is inert anywhere the gem is absent, and dark outside
the supported window.
Defined Under Namespace
Modules: ChatPatch, EmbeddingsPatch
Constant Summary collapse
- SUPPORTED_BELOW =
Gem::Version.new("9.0.0")
- CHAT_RESPONSE =
ruby-openai returns plain parsed-JSON Hashes (string keys): "model"=>..., "choices"=>[{"message"=>{..., "finish_reason"=>...}], "usage"=>...}.
lambda do |response| h = response.is_a?(Hash) ? response : {} usage = h["usage"].is_a?(Hash) ? h["usage"] : {} choices = h["choices"].is_a?(Array) ? h["choices"] : [] LLM.response_attributes( model: h["model"], id: h["id"], finish_reasons: choices.map { |c| c.is_a?(Hash) ? c["finish_reason"] : nil }.compact, input_tokens: usage["prompt_tokens"], output_tokens: usage["completion_tokens"], output_messages: choices.map { |c| c.is_a?(Hash) ? c["message"] : nil }.compact ) end
- EMBEDDINGS_RESPONSE =
Embeddings response: usage.prompt_tokens only; the vectors are NOT content (and would be enormous) — count them, never ship them.
lambda do |response| h = response.is_a?(Hash) ? response : {} usage = h["usage"].is_a?(Hash) ? h["usage"] : {} LLM.response_attributes( model: h["model"], input_tokens: usage["prompt_tokens"] ) end
Class Method Summary collapse
- .chat_request(params) ⇒ Object
- .embeddings_request(params) ⇒ Object
- .install! ⇒ Object
- .installed? ⇒ Boolean
-
.present? ⇒ Boolean
The official
openaigem ALSO defines OpenAI::Client#chat — but as a zero-argument RESOURCE ACCESSOR (client.chat.completions…). - .supported_version? ⇒ Boolean
Class Method Details
.chat_request(params) ⇒ Object
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 60 def chat_request(params) params = params.is_a?(Hash) ? params : {} LLM.request_attributes( provider: "openai", operation: "chat", model: params[:model] || params["model"], max_tokens: params[:max_tokens] || params["max_tokens"] || params[:max_completion_tokens] || params["max_completion_tokens"], input_messages: params[:messages] || params["messages"] ) end |
.embeddings_request(params) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 71 def (params) params = params.is_a?(Hash) ? params : {} # Embeddings INPUT is deliberately NOT captured (fleet parity: # the python core rides contrib openai-v2, which emits no # embeddings input under any attribute — document chunks are a # classic PII carrier, and gen_ai.input.messages is defined for # chat messages, not document batches). Tokens + models carry # the signal; widening this is a fleet ruling, not a shim choice. LLM.request_attributes( provider: "openai", operation: "embeddings", model: params[:model] || params["model"] ) end |
.install! ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 32 def install! return true if @installed ::OpenAI::Client.prepend(ChatPatch) if ::OpenAI::Client.method_defined?(:embeddings) ::OpenAI::Client.prepend(EmbeddingsPatch) end Diagnostics.info("LLM shim installed: ruby-openai (client.chat, client.embeddings)") @installed = true end |
.installed? ⇒ Boolean
43 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 43 def installed? = @installed |
.present? ⇒ Boolean
The official openai gem ALSO defines OpenAI::Client#chat — but
as a zero-argument RESOURCE ACCESSOR (client.chat.completions…).
ruby-openai's is the request method chat(parameters:). The shape
check (the parameters: keyword) is the ONLY reliable discriminator
between the two gems sharing this namespace — never the bare
method name.
22 23 24 25 26 27 28 29 30 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 22 def present? return false unless defined?(::OpenAI::Client) && ::OpenAI::Client.method_defined?(:chat) chat = ::OpenAI::Client.instance_method(:chat) takes_parameters = chat.parameters.any? { |type, name| %i[key keyreq].include?(type) && name == :parameters } takes_parameters && supported_version? rescue StandardError false end |
.supported_version? ⇒ Boolean
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/foam/otel/llm/ruby_openai_shim.rb', line 45 def supported_version? spec = Gem.loaded_specs["ruby-openai"] return true if spec.nil? # vendored/unknown: the shape checks decide supported = spec.version < SUPPORTED_BELOW unless supported || @version_warned @version_warned = true Diagnostics.warn("ruby-openai #{spec.version} is outside foam's LLM-shim window (< #{SUPPORTED_BELOW}) " \ "— the ruby-openai shim stays dark; file this with foam support") end supported rescue StandardError true end |