Module: OpenAICompatibleErrors::Headers
- Defined in:
- lib/openai_compatible_errors/headers.rb
Constant Summary collapse
- MAX_RETRY_AFTER_MS =
86_400_000- MAX_HEADER_VALUE_CHARS =
512- REQUEST_ID_NAMES =
%w[x-request-id request-id x-correlation-id trace-id].freeze
- RETRY_NAMES =
%w[retry-after retry-after-ms x-retry-after-ms].freeze
Class Method Summary collapse
- .materialize(input) ⇒ Object
- .parse_milliseconds(value) ⇒ Object
- .parse_retry_after(value, now:) ⇒ Object
- .request_id(input) ⇒ Object
-
.retry_after_ms(input, now: Time.now) ⇒ Object
Returns a bounded delay.
- .values(input, name) ⇒ Object
Class Method Details
.materialize(input) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/openai_compatible_errors/headers.rb', line 14 def materialize(input) pairs = [] if input.is_a?(Hash) input.each_pair { |key, value| pairs << [key, value] } elsif input.respond_to?(:each_header) begin input.each_header { |key, value| pairs << [key, value] } rescue StandardError pairs = [] end elsif input.respond_to?(:each_pair) begin input.each_pair { |key, value| pairs << [key, value] } rescue StandardError pairs = [] end end result = Hash.new { |hash, key| hash[key] = [] } pairs.each do |key, value| name = key.to_s.strip.downcase next unless name.match?(/\A[a-z0-9][a-z0-9_-]{0,127}\z/) values = value.is_a?(Array) ? value : [value] values.each do |item| text = item.is_a?(String) ? item : item.to_s next if text.empty? || text.bytesize > MAX_HEADER_VALUE_CHARS result[name] << text end end result end |
.parse_milliseconds(value) ⇒ Object
91 92 93 94 95 |
# File 'lib/openai_compatible_errors/headers.rb', line 91 def parse_milliseconds(value) return nil unless value.match?(/\A\d{1,12}\z/) [value.to_i, MAX_RETRY_AFTER_MS].min end |
.parse_retry_after(value, now:) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/openai_compatible_errors/headers.rb', line 97 def parse_retry_after(value, now:) if value.match?(/\A\d{1,8}\z/) return [value.to_i * 1_000, MAX_RETRY_AFTER_MS].min end return nil unless value.bytesize <= 128 begin seconds = Time.httpdate(value).to_f - now.to_f return [[(seconds * 1_000).ceil, 0].max, MAX_RETRY_AFTER_MS].min rescue ArgumentError nil end end |
.request_id(input) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/openai_compatible_errors/headers.rb', line 52 def request_id(input) headers = materialize(input) REQUEST_ID_NAMES.each do |name| headers[name].each do |candidate| value = candidate.strip next unless value.match?(/\A[A-Za-z0-9][A-Za-z0-9._:\/-]{0,255}\z/) next if value.match?(/bearer|api[_ -]?key|secret|token/i) return value end end nil end |
.retry_after_ms(input, now: Time.now) ⇒ Object
Returns a bounded delay. A malformed present hint returns the maximum sentinel so a conservative retry policy fails closed.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/openai_compatible_errors/headers.rb', line 68 def retry_after_ms(input, now: Time.now) headers = materialize(input) candidates = [] malformed = false RETRY_NAMES.each do |name| headers[name].each do |value| parsed = if name.end_with?("-ms") parse_milliseconds(value) else parse_retry_after(value, now: now) end if parsed.nil? malformed = true else candidates << parsed end end end return candidates.max unless candidates.empty? malformed ? MAX_RETRY_AFTER_MS : nil end |
.values(input, name) ⇒ Object
48 49 50 |
# File 'lib/openai_compatible_errors/headers.rb', line 48 def values(input, name) materialize(input)[name.to_s.downcase] end |