class Anthropic::Internal::Transport::BaseClient
@abstract
@api private
def auth_headers = {}
-
(Hash{String=>String})
-
Other tags:
- Api: - private
def auth_headers = {}
def build_request(req, opts)
-
(Hash{Symbol=>Object})
-
Options Hash:
(**opts)
-
:timeout
(Float, nil
) -- -
:max_retries
(Integer, nil
) -- -
:extra_body
(Object, nil
) -- -
:extra_headers
(Hash{String=>String, nil}, nil
) -- -
:extra_query
(Hash{String=>Array
) --, String, nil}, nil -
:idempotency_key
(String, nil
) -- -
:model
(Anthropic::Internal::Type::Converter, Class, nil
) -- -
:stream
(Class, nil
) -- -
:page
(Class, nil
) -- -
:unwrap
(Symbol, nil
) -- -
:body
(Object, nil
) -- -
:headers
(Hash{String=>String, Integer, Array
) --, nil}, nil -
:query
(Hash{String=>Array
) --, String, nil}, nil -
:path
(String, Array
) -- -
:method
(Symbol
) --
Parameters:
-
opts
(Hash{Symbol=>Object}
) -- . -
req
(Hash{Symbol=>Object}
) -- .
Other tags:
- Api: - private
def build_request(req, opts) , uninterpolated_path = req.fetch_values(:method, :path) Anthropic::Internal::Util.interpolate_path(uninterpolated_path) = Anthropic::Internal::Util.deep_merge(req[:query].to_h, opts[:extra_query].to_h) s = Anthropic::Internal::Util.normalized_headers( ders, _headers, :headers].to_h, [:extra_headers].to_h empotency_header && aders.key?(@idempotency_header) && t::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase) ers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key } headers.key?("x-stainless-retry-count") ers["x-stainless-retry-count"] = "0" t = opts.fetch(:timeout, @timeout).to_f.clamp((0..)) headers.key?("x-stainless-timeout") || timeout.zero? ers["x-stainless-timeout"] = timeout.to_s s.reject! { |_, v| v.to_s.empty? } method get | :head | :options | :trace l thropic::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact) s, encoded = Anthropic::Internal::Util.encode_content(headers, body) od: method, Anthropic::Internal::Util.join_parsed_uri(@base_url, {**req, path: path, query: query}), ers: headers, : encoded, retries: opts.fetch(:max_retries, @max_retries), out: timeout
def follow_redirect(request, status:, response_headers:)
-
(Hash{Symbol=>Object})
-
Parameters:
-
response_headers
(Hash{String=>String}, Net::HTTPHeader
) -- -
status
(Integer
) -- -
request
(Hash{Symbol=>Object}
) -- .
Options Hash:
(**request)
-
:timeout
(Float
) -- -
:max_retries
(Integer
) -- -
:body
(Object
) -- -
:headers
(Hash{String=>String}
) -- -
:url
(URI::Generic
) -- -
:method
(Symbol
) --
Other tags:
- Api: - private
def follow_redirect(request, status:, response_headers:) method, url, headers = request.fetch_values(:method, :url, :headers) location = Kernel.then do URI.join(url, response_headers["location"]) rescue ArgumentError message = "Server responded with status #{status} but no valid location header." raise Anthropic::Errors::APIConnectionError.new(url: url, message: message) end request = {**request, url: location} case [url.scheme, location.scheme] in ["https", "http"] message = "Tried to redirect to a insecure URL" raise Anthropic::Errors::APIConnectionError.new(url: url, message: message) else nil end # from whatwg fetch spec case [status, method] in [301 | 302, :post] | [303, _] drop = %w[content-encoding content-language content-length content-location content-type] request = { **request, method: method == :head ? :head : :get, headers: headers.except(*drop), body: nil } else end # from undici if Anthropic::Internal::Util.uri_origin(url) != Anthropic::Internal::Util.uri_origin(location) drop = %w[authorization cookie host proxy-authorization] request = {**request, headers: request.fetch(:headers).except(*drop)} end request end
def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
-
(String)
-
Other tags:
- Api: - private
def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
def initialize(
-
idempotency_header
(String, nil
) -- -
headers
(Hash{String=>String, Integer, Array
) --, nil} -
max_retry_delay
(Float
) -- -
initial_retry_delay
(Float
) -- -
max_retries
(Integer
) -- -
timeout
(Float
) -- -
base_url
(String
) --
Other tags:
- Api: - private
def initialize( base_url:, timeout: 0.0, max_retries: 0, initial_retry_delay: 0.0, max_retry_delay: 0.0, headers: {}, idempotency_header: nil ) @requester = Anthropic::Internal::Transport::PooledNetRequester.new @headers = Anthropic::Internal::Util.normalized_headers( self.class::PLATFORM_HEADERS, { "accept" => "application/json", "content-type" => "application/json" }, headers ) @base_url = Anthropic::Internal::Util.parse_uri(base_url) @idempotency_header = idempotency_header&.to_s&.downcase @max_retries = max_retries @timeout = timeout @initial_retry_delay = initial_retry_delay @max_retry_delay = max_retry_delay end
def inspect
-
(String)
-
def inspect # rubocop:disable Layout/LineLength base_url = Anthropic::Internal::Util.unparse_uri(@base_url) "#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{base_url} max_retries=#{@max_retries} timeout=#{@timeout}>" # rubocop:enable Layout/LineLength end
def reap_connection!(status, stream:)
-
stream
(Enumerable
) --, nil -
status
(Integer, Anthropic::Errors::APIConnectionError
) --
Other tags:
- Api: - private
def reap_connection!(status, stream:) case status in (..199) | (300..499) stream&.each { next } in Anthropic::Errors::APIConnectionError | (500..) Anthropic::Internal::Util.close_fused!(stream) else end end
def request(req)
-
(Object)
-
Raises:
-
(Anthropic::Errors::APIError)
-
Options Hash:
(**options)
-
:timeout
(Float, nil
) -- -
:max_retries
(Integer, nil
) -- -
:extra_body
(Object, nil
) -- -
:extra_headers
(Hash{String=>String, nil}, nil
) -- -
:extra_query
(Hash{String=>Array
) --, String, nil}, nil -
:idempotency_key
(String, nil
) --
Parameters:
-
options
(Anthropic::RequestOptions, Hash{Symbol=>Object}, nil
) -- . -
model
(Anthropic::Internal::Type::Converter, Class, nil
) -- -
stream
(Class, nil
) -- -
page
(Class, nil
) -- -
unwrap
(Symbol, nil
) -- -
body
(Object, nil
) -- -
headers
(Hash{String=>String, Integer, Array
) --, nil}, nil -
query
(Hash{String=>Array
) --, String, nil}, nil -
path
(String, Array
) -- -
method
(Symbol
) --
Overloads:
-
request(method, path, query: {}, headers: {}, body: nil, unwrap: nil, page: nil, stream: nil, model: Anthropic::Internal::Type::Unknown, options: {})
def request(req) self.class.validate!(req) model = req.fetch(:model) { Anthropic::Internal::Type::Unknown } opts = req[:options].to_h Anthropic::RequestOptions.validate!(opts) request = build_request(req.except(:options), opts) url = request.fetch(:url) # Don't send the current retry count in the headers if the caller modified the header defaults. send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0" status, response, stream = send_request( request, redirect_count: 0, retry_count: 0, send_retry_header: send_retry_header ) decoded = Anthropic::Internal::Util.decode_content(response, stream: stream) case req in { stream: Class => st } st.new(model: model, url: url, status: status, response: response, stream: decoded) in { page: Class => page } page.new(client: self, req: req, headers: response, page_data: decoded) else unwrapped = Anthropic::Internal::Util.dig(decoded, req[:unwrap]) Anthropic::Internal::Type::Converter.coerce(model, unwrapped) end end
def retry_delay(headers, retry_count:)
-
(Float)
-
Parameters:
-
retry_count
(Integer
) -- -
headers
(Hash{String=>String}
) --
Other tags:
- Api: - private
def retry_delay(headers, retry_count:) standard extension Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 } span if span header = headers["retry-after"] span if (span = Float(retry_header, exception: false)) retry_header&.then do .httpdate(_1) - Time.now ArgumentError span if span = retry_count**2 = 1 - (0.25 * rand) ial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
def send_request(request, redirect_count:, retry_count:, send_retry_header:)
-
(Array(Integer, Net::HTTPResponse, Enumerable
-))
Raises:
-
(Anthropic::Errors::APIError)
-
Parameters:
-
send_retry_header
(Boolean
) -- -
retry_count
(Integer
) -- -
redirect_count
(Integer
) -- -
request
(Hash{Symbol=>Object}
) -- .
Options Hash:
(**request)
-
:timeout
(Float
) -- -
:max_retries
(Integer
) -- -
:body
(Object
) -- -
:headers
(Hash{String=>String}
) -- -
:url
(URI::Generic
) -- -
:method
(Symbol
) --
Other tags:
- Api: - private
def send_request(request, redirect_count:, retry_count:, send_retry_header:) eaders, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout) = {**request.except(:timeout), deadline: Anthropic::Internal::Util.monotonic_secs + timeout} d_retry_header ers["x-stainless-retry-count"] = retry_count.to_s us, response, stream = @requester.execute(input) Anthropic::Errors::APIConnectionError => e us = e tatus 99 tus, response, stream] ..399 if redirect_count >= self.class::MAX_REDIRECTS .class.reap_connection!(status, stream: stream) age = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects." e Anthropic::Errors::APIConnectionError.new(url: url, message: message) ..399 .class.reap_connection!(status, stream: stream) est = self.class.follow_redirect(request, status: status, response_headers: response) _request( quest, direct_count: redirect_count + 1, try_count: retry_count, nd_retry_header: send_retry_header hropic::Errors::APIConnectionError if retry_count >= max_retries e status 0..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: response) ded = Kernel.then do thropic::Internal::Util.decode_content(response, stream: stream, suppress_error: true) re lf.class.reap_connection!(status, stream: stream) e Anthropic::Errors::APIStatusError.for( l: url, atus: status, dy: decoded, quest: nil, sponse: response 0..) | Anthropic::Errors::APIConnectionError .class.reap_connection!(status, stream: stream) y = retry_delay(response || {}, retry_count: retry_count) p(delay) _request( quest, direct_count: redirect_count, try_count: retry_count + 1, nd_retry_header: send_retry_header
def should_retry?(status, headers:)
-
(Boolean)
-
Parameters:
-
headers
(Hash{String=>String}, Net::HTTPHeader
) -- -
status
(Integer
) --
Other tags:
- Api: - private
def should_retry?(status, headers:) coerced = Anthropic::Internal::Util.coerce_boolean(headers["x-should-retry"]) case [coerced, status] in [true | false, _] coerced in [_, 408 | 409 | 429 | (500..)] # retry on: # 408: timeouts # 409: locks # 429: rate limits # 500+: unknown errors true else false end end
def validate!(req)
-
(ArgumentError)
-
Parameters:
-
req
(Hash{Symbol=>Object}
) --
Other tags:
- Api: - private
def validate!(req) keys = [:method, :path, :query, :headers, :body, :unwrap, :page, :stream, :model, :options] case req in Hash req.each_key do |k| unless keys.include?(k) raise ArgumentError.new("Request `req` keys must be one of #{keys}, got #{k.inspect}") end end else raise ArgumentError.new("Request `req` must be a Hash or RequestOptions, got #{req.inspect}") end end