Class: Aigen::Google::HttpClient

Inherits:
Object
  • Object
show all
Defined in:
lib/aigen/google/http_client.rb

Constant Summary collapse

BASE_URL =
"https://generativelanguage.googleapis.com/v1beta"

Instance Method Summary collapse

Constructor Details

#initialize(api_key:, timeout: 30, retry_count: 3) ⇒ HttpClient

Returns a new instance of HttpClient.



11
12
13
14
15
# File 'lib/aigen/google/http_client.rb', line 11

def initialize(api_key:, timeout: 30, retry_count: 3)
  @api_key = api_key
  @timeout = timeout
  @retry_count = retry_count
end

Instance Method Details

#post(path, payload, attempt: 1) ⇒ Object



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
# File 'lib/aigen/google/http_client.rb', line 17

def post(path, payload, attempt: 1)
  response = connection.post(path) do |req|
    req.headers["Content-Type"] = "application/json"
    req.headers["x-goog-api-key"] = @api_key
    req.body = payload.to_json
  end

  handle_response(response, path, payload, attempt)
rescue Faraday::Error => e
  # Handle timeout-like errors (including WebMock's .to_timeout)
  is_timeout = e.is_a?(Faraday::TimeoutError) ||
    e.is_a?(::Timeout::Error) ||
    e.message.include?("execution expired") ||
    e.message.include?("timed out")

  if is_timeout
    if attempt >= @retry_count
      raise TimeoutError, "Request timed out after #{@retry_count} retries"
    end
    backoff_seconds = 2**(attempt - 1) # 1s, 2s, 4s
    sleep backoff_seconds
    post(path, payload, attempt: attempt + 1)
  else
    # Network connection errors
    raise ServerError.new("Network error: #{e.message}", status_code: nil)
  end
end

#post_stream(path, payload) {|chunk| ... } ⇒ nil

Makes a streaming POST request to the Gemini API. Processes chunked responses and yields each parsed chunk to the provided block.

Examples:

Stream generated content chunks

http_client.post_stream("models/gemini-pro:streamGenerateContent", payload) do |chunk|
  text = chunk["candidates"][0]["content"]["parts"][0]["text"]
  print text
end

Parameters:

  • path (String)

    the API endpoint path

  • payload (Hash)

    the request payload (will be JSON encoded)

Yield Parameters:

  • chunk (Hash)

    parsed JSON chunk from the streaming response

Returns:

  • (nil)

    always returns nil when block is given

Raises:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/aigen/google/http_client.rb', line 65

def post_stream(path, payload, &block)
  raise ArgumentError, "block required for streaming" unless block_given?

  buffer = ""

  response = connection.post(path) do |req|
    req.headers["Content-Type"] = "application/json"
    req.headers["x-goog-api-key"] = @api_key
    req.body = payload.to_json

    req.options.on_data = proc do |chunk, _total_bytes|
      buffer += chunk

      # Process complete lines (newline-delimited JSON)
      while (newline_index = buffer.index("\n"))
        line = buffer.slice!(0, newline_index + 1).strip
        next if line.empty?

        begin
          parsed_chunk = JSON.parse(line)
          block.call(parsed_chunk)
        rescue JSON::ParserError => e
          raise ServerError.new("Invalid JSON in stream chunk: #{e.message}", status_code: nil)
        end
      end
    end
  end

  # Check for non-200 status codes
  handle_stream_response_status(response)

  nil
rescue Faraday::Error => e
  raise ServerError.new("Network error during streaming: #{e.message}", status_code: nil)
end