Class: OllamaChat::Utils::Fetcher

Inherits:
Object
  • Object
show all
Defined in:
lib/ollama_chat/utils/fetcher.rb

Overview

A fetcher implementation that handles retrieval and caching of HTTP resources.

This class provides functionality to fetch content from URLs, with support for caching responses and their metadata. It handles various content types and integrates with different cache backends to improve performance by avoiding redundant network requests.

Examples:

Fetching content from a URL with caching

fetcher = OllamaChat::Utils::Fetcher.new(cache: redis_cache)
fetcher.get('https://example.com/data.json') do |tmp|
  # Process the fetched content
end

Defined Under Namespace

Modules: HeaderExtension Classes: RetryWithoutStreaming

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(debug: false, http_options: {}) ⇒ Fetcher

The initialize method sets up the fetcher instance with debugging and HTTP configuration options.

Parameters:

  • debug (TrueClass, FalseClass) (defaults to: false)

    enables or disables debug output

  • http_options (Hash) (defaults to: {})

    additional options to pass to the HTTP client



181
182
183
184
185
186
# File 'lib/ollama_chat/utils/fetcher.rb', line 181

def initialize(debug: false, http_options: {})
  @debug        = debug
  @started      = false
  @streaming    = true
  @http_options = http_options
end

Class Method Details

.execute(command) {|tmpfile| ... } ⇒ Object

The execute method runs a shell command and processes its output.

It captures the command’s standard output and error streams, writes them to a temporary file, and yields the file to the caller. If an exception occurs during execution, it reports the error and yields a failed temporary file instead.

Parameters:

  • command (String)

    the shell command to execute

Yields:

  • (tmpfile)


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ollama_chat/utils/fetcher.rb', line 153

def self.execute(command, &block)
  Tempfile.open do |tmp|
    unless command =~ /2>&1/
      command += ' 2>&1'
    end
    IO.popen(command) do |io|
      until io.eof?
        tmp.write io.read(1 << 14)
      end
      tmp.rewind
      tmp.extend(OllamaChat::Utils::Fetcher::HeaderExtension)
      tmp.content_type = MIME::Types['text/plain'].first
      block.(tmp)
    end
  end
rescue => e
  STDERR.puts "Cannot execute #{command.inspect} (#{e})"
  if @debug && !e.is_a?(RuntimeError)
    STDERR.puts "#{e.backtrace * ?\n}"
  end
  yield HeaderExtension.failed
end

.get(url, headers: {}, **options) {|tmp| ... } ⇒ Object?

The get method retrieves content from a URL, using caching when available. It processes the URL with optional headers and additional options, then yields a temporary file containing the retrieved content. If caching is enabled and content is found in the cache, it returns the cached result instead of fetching again. The method handles both cached and fresh fetches, ensuring that cache is updated when new content is retrieved.

Parameters:

  • url (String)

    the URL to fetch content from

  • headers (Hash) (defaults to: {})

    optional headers to include in the request

  • options (Hash)

    additional options for the fetch operation

Yields:

  • (tmp)

Returns:

  • (Object)

    the result of the block execution

  • (nil)

    if no block is given or if the fetch fails



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ollama_chat/utils/fetcher.rb', line 90

def self.get(url, headers: {}, **options, &block)
  cache = options.delete(:cache) and
    cache = OllamaChat::Utils::CacheFetcher.new(cache)
  cache and infobar.puts "Getting #{url.to_s.inspect} via cache…"
  if result = cache&.get(url, &block)
    content_type = result&.content_type || 'unknown'
    infobar.puts "…hit, found #{content_type} content in cache."
    return result
  else
    new(**options).send(:get, url, headers:) do |tmp|
      result = block.(tmp)
      if cache && !tmp.is_a?(StringIO)
        tmp.rewind
        cache.put(url, tmp)
      end
      result
    end
  end
end

.normalize_url(url) ⇒ Object

The normalize_url method processes a URL by converting it to a string, decoding any URI components, removing anchors, and then escaping the URL to ensure it is properly formatted.



113
114
115
116
117
118
# File 'lib/ollama_chat/utils/fetcher.rb', line 113

def self.normalize_url(url)
  url = url.to_s
  url = URI.decode_uri_component(url)
  url = url.sub(/#.*/, '')
  URI::Parser.new.escape(url).to_s
end

.read(filename) {|file| ... } ⇒ nil, Object

The read method opens a file and extends it with header extension metadata. It then yields the file to the provided block for processing. If the file does not exist, it outputs an error message to standard error.

Parameters:

  • filename (String)

    the path to the file to be read

Yields:

  • (file)

    yields the opened file with header extension

Returns:

  • (nil)

    returns nil if the file does not exist

  • (Object)

    returns the result of the block execution if the file exists



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/ollama_chat/utils/fetcher.rb', line 131

def self.read(filename, &block)
  if File.exist?(filename)
    File.open(filename) do |file|
      file.extend(OllamaChat::Utils::Fetcher::HeaderExtension)
      file.content_type = MIME::Types.type_for(filename).first
      block.(file)
    end
  else
    STDERR.puts "File #{filename.to_s.inspect} doesn't exist."
  end
end

Instance Method Details

#get(url, **opts) {|Tempfile| ... } ⇒ Object

Makes an HTTP GET request to the specified URL with optional headers and processing block.

This method handles both streaming and non-streaming HTTP requests, using Excon for the actual HTTP communication. The response body is written to a temporary file which is then decorated with additional behavior before being passed to the provided block.

Parameters:

  • url (String)

    The URL to make the GET request to

Yields:

  • (Tempfile)

    The temporary file containing the response body, after decoration



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
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/ollama_chat/utils/fetcher.rb', line 199

def get(url, **opts, &block)
  opts.delete(:response_block) and raise ArgumentError, 'response_block not allowed'
  middlewares = (self.middlewares | Array((opts.delete(:middlewares)))).uniq
  headers = opts.delete(:headers) || {}
  headers |= self.headers
  headers = headers.transform_keys(&:to_s)
  response = nil
  Tempfile.open do |tmp|
    infobar.label = 'Getting'
    if @streaming
      response = excon(url, headers:, response_block: callback(tmp), **opts).request(method: :get)
      response.status != 200 || !@started and raise RetryWithoutStreaming
      decorate_io(tmp, response)
      infobar.finish
      block.(tmp)
    else
      response = excon(url, headers:, middlewares:, **opts).request(method: :get)
      if response.status != 200
        raise "invalid response status code"
      end
      body = response.body
      tmp.print body
      infobar.update(message: message(body.size, body.size), force: true)
      decorate_io(tmp, response)
      infobar.finish
      block.(tmp)
    end
  end
rescue RetryWithoutStreaming
  @streaming = false
  retry
rescue => e
  STDERR.puts "Cannot get #{url.to_s.inspect} (#{e}): #{response&.status_line || 'n/a'}"
  if @debug && !e.is_a?(RuntimeError)
    STDERR.puts "#{e.backtrace * ?\n}"
  end
  yield HeaderExtension.failed
end