Class: Anthropic::APIResponse
- Inherits:
-
Object
- Object
- Anthropic::APIResponse
- Defined in:
- lib/anthropic/middleware.rb
Overview
The per-attempt response object a middleware sees.
‘nxt.call(req)` returns an `APIResponse` for all HTTP status codes —4xx and 5xx included. It does not raise on error status; the SDK converts to a typed `APIError` only after the chain completes. This lets a middleware inspect `res.status` on a 5xx and choose to substitute, retry differently, or pass through. Connection-level failures (Errors::APITimeoutError, Errors::APIConnectionError) do raise from `nxt.call`.
Defined Under Namespace
Classes: ConsumedBodyError
Instance Attribute Summary collapse
-
#headers ⇒ Hash{String=>String}
readonly
Normalized response headers.
-
#raw ⇒ Net::HTTPResponse?
readonly
The raw response.
-
#request ⇒ Anthropic::APIRequest?
readonly
The request that produced this response.
-
#status ⇒ Integer
readonly
HTTP status code.
Class Method Summary collapse
-
.wrap(status, raw, stream, request: nil) ⇒ Anthropic::APIResponse
private
Wraps the ‘[status, Net::HTTPResponse, Enumerable<String>]` tuple the pooled requester returns.
Instance Method Summary collapse
-
#body ⇒ Enumerable<String>
The response body.
-
#buffer!(force: false) ⇒ self
Drain the body into memory so it can be re-read.
-
#initialize(status:, headers: {}, body: nil, raw: nil, streaming: nil, request: nil) ⇒ APIResponse
constructor
A new instance of APIResponse.
-
#parse ⇒ Object
Decode and coerce the body to the SDK-typed result the original caller would have received (e.g. ‘Anthropic::Message`).
-
#retryable? ⇒ Boolean
Whether the SDK would retry this status under its default policy — parity with the Python SDK’s ‘Response.is_retryable`.
-
#streaming? ⇒ Boolean
Whether the request that produced this response is a streaming/SSE request.
-
#to_tuple ⇒ Array(Integer, Net::HTTPResponse|nil, Hash{String=>String}, Enumerable<String>)
private
Back to the ‘[Integer, Net::HTTPResponse|nil, Hash, Enumerable<String>]` tuple shape `send_request` works with internally.
-
#wrap_body(streaming: streaming?) ) {|upstream| ... } ⇒ Anthropic::APIResponse
Replace the body with the block’s return value — the composable way to intercept streamed chunks without buffering.
Constructor Details
#initialize(status:, headers: {}, body: nil, raw: nil, streaming: nil, request: nil) ⇒ APIResponse
Returns a new instance of APIResponse.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/anthropic/middleware.rb', line 195 def initialize(status:, headers: {}, body: nil, raw: nil, streaming: nil, request: nil) @status = status @headers = Anthropic::Internal::Util.normalized_headers(headers) @raw = raw @request = request @streaming = streaming @buffered = nil @drained = false @parsed = Anthropic::Internal::OMIT @body = case body in nil @buffered = [].freeze in String @buffered = [body].freeze in Array @buffered = body.dup.freeze else # Wrap once so the first pull flips `@drained`; `to_tuple`/`buffer!` # then detect a body that was iterated without buffering. Fused so # `Util.close_fused!` (stream `close`, connection reaping) propagates # to the upstream enum without first pulling a chunk off the socket. Anthropic::Internal::Util.chain_fused(body) do |y| @drained = true body.each { y << _1 } end end end |
Instance Attribute Details
#headers ⇒ Hash{String=>String} (readonly)
Returns normalized response headers.
161 162 163 |
# File 'lib/anthropic/middleware.rb', line 161 def headers @headers end |
#raw ⇒ Net::HTTPResponse? (readonly)
Returns the raw response. ‘nil` for an `APIResponse.new(…)` constructed by a middleware (e.g. a mock).
165 166 167 |
# File 'lib/anthropic/middleware.rb', line 165 def raw @raw end |
#request ⇒ Anthropic::APIRequest? (readonly)
Returns the request that produced this response. #parse uses it to recover the SDK return type; a fabricated response without one parses to the decoded body only.
170 171 172 |
# File 'lib/anthropic/middleware.rb', line 170 def request @request end |
#status ⇒ Integer (readonly)
Returns HTTP status code.
158 159 160 |
# File 'lib/anthropic/middleware.rb', line 158 def status @status end |
Class Method Details
.wrap(status, raw, stream, request: nil) ⇒ Anthropic::APIResponse
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Wraps the ‘[status, Net::HTTPResponse, Enumerable<String>]` tuple the pooled requester returns.
182 183 184 185 186 |
# File 'lib/anthropic/middleware.rb', line 182 def self.wrap(status, raw, stream, request: nil) # `initialize` normalizes `headers`, so pass the raw header hash straight # through rather than normalizing twice on every response. new(status: status, headers: raw.each_header.to_h, body: stream, raw: raw, request: request) end |
Instance Method Details
#body ⇒ Enumerable<String>
Returns the response body. Single-consumer unless #buffer! has been called. After ‘buffer!`, this is a rewindable `Array`.
228 |
# File 'lib/anthropic/middleware.rb', line 228 def body = @buffered || @body |
#buffer!(force: false) ⇒ self
Drain the body into memory so it can be re-read. Idempotent once buffered.
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/anthropic/middleware.rb', line 254 def buffer!(force: false) return self if @buffered if streaming? && !force raise ArgumentError, "Refusing to buffer a streaming response; pass `force: true` " \ "or use `wrap_body` to transform the stream without buffering." end if @drained raise ConsumedBodyError, "Response body was already consumed. Call `buffer!` (or " \ "`parse`) before reading the body, or use `wrap_body` to " \ "transform the stream without consuming it." end @buffered = @body.to_a.freeze @drained = true self end |
#parse ⇒ Object
Decode and coerce the body to the SDK-typed result the original caller would have received (e.g. ‘Anthropic::Message`). Internally `buffer!`s first, so calling `parse` does not steal the body from downstream — the SDK reuses the buffered copy.
For non-streaming responses the decoded value is coerced to the request’s return type and memoized — repeated calls across the chain cost a single decode. A fabricated response without a ‘request:` parses to the decoded body (e.g. a `Hash`) instead of an SDK model.
For streaming requests, returns a typed stream reading an independent buffered copy of the response body — iterating (or ‘break`ing out of) it neither consumes nor cancels the events the SDK caller will read. Streams are single-consumer, so each call returns a fresh stream rather than a memoized one.
319 320 321 322 323 324 325 326 327 328 |
# File 'lib/anthropic/middleware.rb', line 319 def parse return parse_stream if streaming? return @parsed unless Anthropic::Internal::OMIT.equal?(@parsed) buffer! decoded = Anthropic::Internal::Util.decode_content(@headers, stream: body.each) unwrapped = Anthropic::Internal::Util.dig(decoded, @request&.unwrap) cast_to = @request&.cast_to || Anthropic::Internal::Type::Unknown @parsed = Anthropic::Internal::Type::Converter.coerce(cast_to, unwrapped) end |
#retryable? ⇒ Boolean
Returns whether the SDK would retry this status under its default policy — parity with the Python SDK’s ‘Response.is_retryable`. Useful for middleware that wants to match the SDK’s own retry decision.
240 241 242 |
# File 'lib/anthropic/middleware.rb', line 240 def retryable? Anthropic::Internal::Transport::BaseClient.should_retry?(@status, headers: @headers) end |
#streaming? ⇒ Boolean
Returns whether the request that produced this response is a streaming/SSE request.
232 233 234 235 |
# File 'lib/anthropic/middleware.rb', line 232 def streaming? return @streaming unless @streaming.nil? @request&.streaming? || false end |
#to_tuple ⇒ Array(Integer, Net::HTTPResponse|nil, Hash{String=>String}, Enumerable<String>)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Back to the ‘[Integer, Net::HTTPResponse|nil, Hash, Enumerable<String>]` tuple shape `send_request` works with internally. Headers come from #headers — never re-derived from `raw` — so a middleware that returns a modified header set over the original raw response is honored.
338 339 340 341 342 343 344 345 |
# File 'lib/anthropic/middleware.rb', line 338 def to_tuple if @buffered.nil? && @drained raise ConsumedBodyError, "Middleware consumed the response body without buffering it. " \ "Call `buffer!` (or `parse`) before reading, or use `wrap_body`." end [status, @raw, @headers, @buffered || @body] end |
#wrap_body(streaming: streaming?) ) {|upstream| ... } ⇒ Anthropic::APIResponse
Replace the body with the block’s return value — the composable way to intercept streamed chunks without buffering.
283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/anthropic/middleware.rb', line 283 def wrap_body(streaming: streaming?) raise ArgumentError, "wrap_body requires a block" unless block_given? self.class.new( status: @status, headers: @headers, body: yield(body), raw: @raw, streaming: streaming, request: @request ) end |