Module: RubyLLM::Providers::OpenRouter::Chat
- Included in:
- RubyLLM::Providers::OpenRouter
- Defined in:
- lib/ruby_llm/providers/openrouter/chat.rb
Overview
Chat methods of the OpenRouter API integration
Class Method Summary collapse
- .build_reasoning(thinking) ⇒ Object
- .extract_thinking_signature(message_data) ⇒ Object
- .extract_thinking_text(message_data) ⇒ Object
- .format_messages(messages) ⇒ Object
- .format_role(role) ⇒ Object
- .format_thinking(msg) ⇒ Object
- .parse_completion_response(response) ⇒ Object
-
.render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists.
Class Method Details
.build_reasoning(thinking) ⇒ Object
89 90 91 92 93 94 95 96 97 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 89 def build_reasoning(thinking) return nil unless thinking&.enabled? reasoning = {} reasoning[:effort] = thinking.effort if thinking.respond_to?(:effort) && thinking.effort reasoning[:max_tokens] = thinking.budget if thinking.respond_to?(:budget) && thinking.budget reasoning[:enabled] = true if reasoning.empty? reasoning end |
.extract_thinking_signature(message_data) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 139 def extract_thinking_signature() details = ['reasoning_details'] return nil unless details.is_a?(Array) signature = details.filter_map do |detail| detail['signature'] if detail['signature'].is_a?(String) end.first return signature if signature encrypted = details.find { |detail| detail['type'] == 'reasoning.encrypted' && detail['data'].is_a?(String) } encrypted&.dig('data') end |
.extract_thinking_text(message_data) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 120 def extract_thinking_text() candidate = ['reasoning'] return candidate if candidate.is_a?(String) details = ['reasoning_details'] return nil unless details.is_a?(Array) text = details.filter_map do |detail| case detail['type'] when 'reasoning.text' detail['text'] when 'reasoning.summary' detail['summary'] end end.join text.empty? ? nil : text end |
.format_messages(messages) ⇒ Object
69 70 71 72 73 74 75 76 77 78 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 69 def () .map do |msg| { role: format_role(msg.role), content: OpenAI::Media.format_content(msg.content), tool_calls: OpenAI::Tools.format_tool_calls(msg.tool_calls), tool_call_id: msg.tool_call_id }.compact.merge(format_thinking(msg)) end end |
.format_role(role) ⇒ Object
80 81 82 83 84 85 86 87 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 80 def format_role(role) case role when :system @config.openai_use_system_role ? 'system' : 'developer' else role.to_s end end |
.format_thinking(msg) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 99 def format_thinking(msg) thinking = msg.thinking return {} unless thinking && msg.role == :assistant details = [] if thinking.text details << { type: 'reasoning.text', text: thinking.text, signature: thinking.signature }.compact elsif thinking.signature details << { type: 'reasoning.encrypted', data: thinking.signature } end details.empty? ? {} : { reasoning_details: details } end |
.parse_completion_response(response) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 39 def parse_completion_response(response) data = response.body return if data.empty? raise Error.new(response, data.dig('error', 'message')) if data.dig('error', 'message') = data.dig('choices', 0, 'message') return unless usage = data['usage'] || {} cached_tokens = usage.dig('prompt_tokens_details', 'cached_tokens') thinking_tokens = usage.dig('completion_tokens_details', 'reasoning_tokens') thinking_text = extract_thinking_text() thinking_signature = extract_thinking_signature() Message.new( role: :assistant, content: ['content'], thinking: Thinking.build(text: thinking_text, signature: thinking_signature), tool_calls: OpenAI::Tools.parse_tool_calls(['tool_calls']), input_tokens: usage['prompt_tokens'], output_tokens: usage['completion_tokens'], cached_tokens: cached_tokens, cache_creation_tokens: 0, thinking_tokens: thinking_tokens, model_id: data['model'], raw: response ) end |
.render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/ruby_llm/providers/openrouter/chat.rb', line 10 def render_payload(, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) # rubocop:disable Metrics/ParameterLists payload = { model: model.id, messages: (), stream: stream } payload[:temperature] = temperature unless temperature.nil? payload[:tools] = tools.map { |_, tool| OpenAI::Tools.tool_for(tool) } if tools.any? if schema strict = schema[:strict] != false payload[:response_format] = { type: 'json_schema', json_schema: { name: 'response', schema: schema, strict: strict } } end reasoning = build_reasoning(thinking) payload[:reasoning] = reasoning if reasoning payload[:stream_options] = { include_usage: true } if stream payload end |