ollama-ruby 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGES.md +39 -0
 - data/README.md +68 -142
 - data/Rakefile +3 -15
 - data/bin/ollama_cli +37 -6
 - data/lib/ollama/dto.rb +4 -0
 - data/lib/ollama/version.rb +1 -1
 - data/lib/ollama.rb +0 -7
 - data/ollama-ruby.gemspec +9 -20
 - data/spec/ollama/message_spec.rb +9 -0
 - metadata +9 -191
 - data/bin/ollama_chat +0 -1249
 - data/config/redis.conf +0 -5
 - data/docker-compose.yml +0 -10
 - data/lib/ollama/utils/cache_fetcher.rb +0 -38
 - data/lib/ollama/utils/chooser.rb +0 -52
 - data/lib/ollama/utils/fetcher.rb +0 -175
 - data/lib/ollama/utils/file_argument.rb +0 -34
 - data/spec/assets/prompt.txt +0 -1
 - data/spec/ollama/utils/cache_fetcher_spec.rb +0 -43
 - data/spec/ollama/utils/fetcher_spec.rb +0 -137
 - data/spec/ollama/utils/file_argument_spec.rb +0 -17
 
    
        data/config/redis.conf
    DELETED
    
    
    
        data/docker-compose.yml
    DELETED
    
    
| 
         @@ -1,38 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'digest/md5'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            class Ollama::Utils::CacheFetcher
         
     | 
| 
       4 
     | 
    
         
            -
              def initialize(cache)
         
     | 
| 
       5 
     | 
    
         
            -
                @cache = cache
         
     | 
| 
       6 
     | 
    
         
            -
              end
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
              def get(url, &block)
         
     | 
| 
       9 
     | 
    
         
            -
                block or raise ArgumentError, 'require block argument'
         
     | 
| 
       10 
     | 
    
         
            -
                body         = @cache[key(:body, url)]
         
     | 
| 
       11 
     | 
    
         
            -
                content_type = @cache[key(:content_type, url)]
         
     | 
| 
       12 
     | 
    
         
            -
                content_type = MIME::Types[content_type].first
         
     | 
| 
       13 
     | 
    
         
            -
                if body && content_type
         
     | 
| 
       14 
     | 
    
         
            -
                  io = StringIO.new(body)
         
     | 
| 
       15 
     | 
    
         
            -
                  io.rewind
         
     | 
| 
       16 
     | 
    
         
            -
                  io.extend(Ollama::Utils::Fetcher::HeaderExtension)
         
     | 
| 
       17 
     | 
    
         
            -
                  io.content_type = content_type
         
     | 
| 
       18 
     | 
    
         
            -
                  block.(io)
         
     | 
| 
       19 
     | 
    
         
            -
                end
         
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
              def put(url, io)
         
     | 
| 
       23 
     | 
    
         
            -
                io.rewind
         
     | 
| 
       24 
     | 
    
         
            -
                body = io.read
         
     | 
| 
       25 
     | 
    
         
            -
                body.empty? and return
         
     | 
| 
       26 
     | 
    
         
            -
                content_type = io.content_type
         
     | 
| 
       27 
     | 
    
         
            -
                content_type.nil? and return
         
     | 
| 
       28 
     | 
    
         
            -
                @cache.set(key(:body, url), body, ex: io.ex)
         
     | 
| 
       29 
     | 
    
         
            -
                @cache.set(key(:content_type,  url), content_type.to_s, ex: io.ex)
         
     | 
| 
       30 
     | 
    
         
            -
                self
         
     | 
| 
       31 
     | 
    
         
            -
              end
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
              private
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
              def key(type, url)
         
     | 
| 
       36 
     | 
    
         
            -
                [ type, Digest::MD5.hexdigest(url) ] * ?-
         
     | 
| 
       37 
     | 
    
         
            -
              end
         
     | 
| 
       38 
     | 
    
         
            -
            end
         
     | 
    
        data/lib/ollama/utils/chooser.rb
    DELETED
    
    | 
         @@ -1,52 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'amatch'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'search_ui'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'term/ansicolor'
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            module Ollama::Utils::Chooser
         
     | 
| 
       6 
     | 
    
         
            -
              include SearchUI
         
     | 
| 
       7 
     | 
    
         
            -
              include Term::ANSIColor
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
              module_function
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
              # The choose method presents a list of entries and prompts the user
         
     | 
| 
       12 
     | 
    
         
            -
              # for input, allowing them to select one entry based on their input.
         
     | 
| 
       13 
     | 
    
         
            -
              #
         
     | 
| 
       14 
     | 
    
         
            -
              # @param entries [Array] the list of entries to present to the user
         
     | 
| 
       15 
     | 
    
         
            -
              # @param prompt [String] the prompt message to display when asking for input (default: 'Search? %s')
         
     | 
| 
       16 
     | 
    
         
            -
              # @param return_immediately [Boolean] whether to immediately return the first entry if there is only one or nil when there is none (default: false)
         
     | 
| 
       17 
     | 
    
         
            -
              #
         
     | 
| 
       18 
     | 
    
         
            -
              # @return [Object] the selected entry, or nil if no entry was chosen
         
     | 
| 
       19 
     | 
    
         
            -
              #
         
     | 
| 
       20 
     | 
    
         
            -
              # @example
         
     | 
| 
       21 
     | 
    
         
            -
              #   choose(['entry1', 'entry2'], prompt: 'Choose an option:')
         
     | 
| 
       22 
     | 
    
         
            -
              def choose(entries, prompt: 'Search? %s', return_immediately: false)
         
     | 
| 
       23 
     | 
    
         
            -
                if return_immediately && entries.size <= 1
         
     | 
| 
       24 
     | 
    
         
            -
                  return entries.first
         
     | 
| 
       25 
     | 
    
         
            -
                end
         
     | 
| 
       26 
     | 
    
         
            -
                entry = Search.new(
         
     | 
| 
       27 
     | 
    
         
            -
                  prompt:,
         
     | 
| 
       28 
     | 
    
         
            -
                  match: -> answer {
         
     | 
| 
       29 
     | 
    
         
            -
                    matcher = Amatch::PairDistance.new(answer.downcase)
         
     | 
| 
       30 
     | 
    
         
            -
                    matches = entries.map { |n| [ n, -matcher.similar(n.to_s.downcase) ] }.
         
     | 
| 
       31 
     | 
    
         
            -
                      select { |_, s| s < 0 }.sort_by(&:last).map(&:first)
         
     | 
| 
       32 
     | 
    
         
            -
                    matches.empty? and matches = entries
         
     | 
| 
       33 
     | 
    
         
            -
                    matches.first(Tins::Terminal.lines - 1)
         
     | 
| 
       34 
     | 
    
         
            -
                  },
         
     | 
| 
       35 
     | 
    
         
            -
                  query: -> _answer, matches, selector {
         
     | 
| 
       36 
     | 
    
         
            -
                    matches.each_with_index.map { |m, i|
         
     | 
| 
       37 
     | 
    
         
            -
                      i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : "  #{m.to_s}"
         
     | 
| 
       38 
     | 
    
         
            -
                    } * ?\n
         
     | 
| 
       39 
     | 
    
         
            -
                  },
         
     | 
| 
       40 
     | 
    
         
            -
                  found: -> _answer, matches, selector {
         
     | 
| 
       41 
     | 
    
         
            -
                    matches[selector]
         
     | 
| 
       42 
     | 
    
         
            -
                  },
         
     | 
| 
       43 
     | 
    
         
            -
                  output: STDOUT
         
     | 
| 
       44 
     | 
    
         
            -
                ).start
         
     | 
| 
       45 
     | 
    
         
            -
                if entry
         
     | 
| 
       46 
     | 
    
         
            -
                  entry
         
     | 
| 
       47 
     | 
    
         
            -
                else
         
     | 
| 
       48 
     | 
    
         
            -
                  print clear_screen, move_home
         
     | 
| 
       49 
     | 
    
         
            -
                  nil
         
     | 
| 
       50 
     | 
    
         
            -
                end
         
     | 
| 
       51 
     | 
    
         
            -
              end
         
     | 
| 
       52 
     | 
    
         
            -
            end
         
     | 
    
        data/lib/ollama/utils/fetcher.rb
    DELETED
    
    | 
         @@ -1,175 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'tempfile'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'tins/unit'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'infobar'
         
     | 
| 
       4 
     | 
    
         
            -
            require 'mime-types'
         
     | 
| 
       5 
     | 
    
         
            -
            require 'stringio'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'ollama/utils/cache_fetcher'
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
            class Ollama::Utils::Fetcher
         
     | 
| 
       9 
     | 
    
         
            -
              module HeaderExtension
         
     | 
| 
       10 
     | 
    
         
            -
                attr_accessor :content_type
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                attr_accessor :ex
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                def self.failed
         
     | 
| 
       15 
     | 
    
         
            -
                  object = StringIO.new.extend(self)
         
     | 
| 
       16 
     | 
    
         
            -
                  object.content_type = MIME::Types['text/plain'].first
         
     | 
| 
       17 
     | 
    
         
            -
                  object
         
     | 
| 
       18 
     | 
    
         
            -
                end
         
     | 
| 
       19 
     | 
    
         
            -
              end
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
              class RetryWithoutStreaming < StandardError; end
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
              def self.get(url, **options, &block)
         
     | 
| 
       24 
     | 
    
         
            -
                cache = options.delete(:cache) and
         
     | 
| 
       25 
     | 
    
         
            -
                  cache = Ollama::Utils::CacheFetcher.new(cache)
         
     | 
| 
       26 
     | 
    
         
            -
                if result = cache&.get(url, &block)
         
     | 
| 
       27 
     | 
    
         
            -
                  infobar.puts "Getting #{url.to_s.inspect} from cache."
         
     | 
| 
       28 
     | 
    
         
            -
                  return result
         
     | 
| 
       29 
     | 
    
         
            -
                else
         
     | 
| 
       30 
     | 
    
         
            -
                  new(**options).send(:get, url) do |tmp|
         
     | 
| 
       31 
     | 
    
         
            -
                    result = block.(tmp)
         
     | 
| 
       32 
     | 
    
         
            -
                    if cache && !tmp.is_a?(StringIO)
         
     | 
| 
       33 
     | 
    
         
            -
                      tmp.rewind
         
     | 
| 
       34 
     | 
    
         
            -
                      cache.put(url, tmp)
         
     | 
| 
       35 
     | 
    
         
            -
                    end
         
     | 
| 
       36 
     | 
    
         
            -
                    result
         
     | 
| 
       37 
     | 
    
         
            -
                  end
         
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
       39 
     | 
    
         
            -
              end
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
              def self.normalize_url(url)
         
     | 
| 
       42 
     | 
    
         
            -
                url = url.to_s
         
     | 
| 
       43 
     | 
    
         
            -
                url = URI.decode_uri_component(url)
         
     | 
| 
       44 
     | 
    
         
            -
                url = url.sub(/#.*/, '')
         
     | 
| 
       45 
     | 
    
         
            -
                URI::Parser.new.escape(url).to_s
         
     | 
| 
       46 
     | 
    
         
            -
              end
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
              def self.read(filename, &block)
         
     | 
| 
       49 
     | 
    
         
            -
                if File.exist?(filename)
         
     | 
| 
       50 
     | 
    
         
            -
                  File.open(filename) do |file|
         
     | 
| 
       51 
     | 
    
         
            -
                    file.extend(Ollama::Utils::Fetcher::HeaderExtension)
         
     | 
| 
       52 
     | 
    
         
            -
                    file.content_type = MIME::Types.type_for(filename).first
         
     | 
| 
       53 
     | 
    
         
            -
                    block.(file)
         
     | 
| 
       54 
     | 
    
         
            -
                  end
         
     | 
| 
       55 
     | 
    
         
            -
                else
         
     | 
| 
       56 
     | 
    
         
            -
                  STDERR.puts "File #{filename.to_s.inspect} doesn't exist."
         
     | 
| 
       57 
     | 
    
         
            -
                end
         
     | 
| 
       58 
     | 
    
         
            -
              end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
              def self.execute(command, &block)
         
     | 
| 
       61 
     | 
    
         
            -
                Tempfile.open do |tmp|
         
     | 
| 
       62 
     | 
    
         
            -
                  IO.popen(command) do |command|
         
     | 
| 
       63 
     | 
    
         
            -
                    until command.eof?
         
     | 
| 
       64 
     | 
    
         
            -
                      tmp.write command.read(1 << 14)
         
     | 
| 
       65 
     | 
    
         
            -
                    end
         
     | 
| 
       66 
     | 
    
         
            -
                    tmp.rewind
         
     | 
| 
       67 
     | 
    
         
            -
                    tmp.extend(Ollama::Utils::Fetcher::HeaderExtension)
         
     | 
| 
       68 
     | 
    
         
            -
                    tmp.content_type = MIME::Types['text/plain'].first
         
     | 
| 
       69 
     | 
    
         
            -
                    block.(tmp)
         
     | 
| 
       70 
     | 
    
         
            -
                  end
         
     | 
| 
       71 
     | 
    
         
            -
                end
         
     | 
| 
       72 
     | 
    
         
            -
              rescue => e
         
     | 
| 
       73 
     | 
    
         
            -
                STDERR.puts "Cannot execute #{command.inspect} (#{e})"
         
     | 
| 
       74 
     | 
    
         
            -
                if @debug && !e.is_a?(RuntimeError)
         
     | 
| 
       75 
     | 
    
         
            -
                  STDERR.puts "#{e.backtrace * ?\n}"
         
     | 
| 
       76 
     | 
    
         
            -
                end
         
     | 
| 
       77 
     | 
    
         
            -
                yield HeaderExtension.failed
         
     | 
| 
       78 
     | 
    
         
            -
              end
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
              def initialize(debug: false, http_options: {})
         
     | 
| 
       81 
     | 
    
         
            -
                @debug        = debug
         
     | 
| 
       82 
     | 
    
         
            -
                @started      = false
         
     | 
| 
       83 
     | 
    
         
            -
                @streaming    = true
         
     | 
| 
       84 
     | 
    
         
            -
                @http_options = http_options
         
     | 
| 
       85 
     | 
    
         
            -
              end
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
              private
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
              def excon(url, **options)
         
     | 
| 
       90 
     | 
    
         
            -
                url = self.class.normalize_url(url)
         
     | 
| 
       91 
     | 
    
         
            -
                Excon.new(url, options.merge(@http_options))
         
     | 
| 
       92 
     | 
    
         
            -
              end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
              def get(url, &block)
         
     | 
| 
       95 
     | 
    
         
            -
                response = nil
         
     | 
| 
       96 
     | 
    
         
            -
                Tempfile.open do |tmp|
         
     | 
| 
       97 
     | 
    
         
            -
                  infobar.label = 'Getting'
         
     | 
| 
       98 
     | 
    
         
            -
                  if @streaming
         
     | 
| 
       99 
     | 
    
         
            -
                    response = excon(url, headers:, response_block: callback(tmp)).request(method: :get)
         
     | 
| 
       100 
     | 
    
         
            -
                    response.status != 200 || !@started and raise RetryWithoutStreaming
         
     | 
| 
       101 
     | 
    
         
            -
                    decorate_io(tmp, response)
         
     | 
| 
       102 
     | 
    
         
            -
                    infobar.finish
         
     | 
| 
       103 
     | 
    
         
            -
                    block.(tmp)
         
     | 
| 
       104 
     | 
    
         
            -
                  else
         
     | 
| 
       105 
     | 
    
         
            -
                    response = excon(url, headers:, middlewares:).request(method: :get)
         
     | 
| 
       106 
     | 
    
         
            -
                    if response.status != 200
         
     | 
| 
       107 
     | 
    
         
            -
                      raise "invalid response status code"
         
     | 
| 
       108 
     | 
    
         
            -
                    end
         
     | 
| 
       109 
     | 
    
         
            -
                    body = response.body
         
     | 
| 
       110 
     | 
    
         
            -
                    tmp.print body
         
     | 
| 
       111 
     | 
    
         
            -
                    infobar.update(message: message(body.size, body.size), force: true)
         
     | 
| 
       112 
     | 
    
         
            -
                    decorate_io(tmp, response)
         
     | 
| 
       113 
     | 
    
         
            -
                    infobar.finish
         
     | 
| 
       114 
     | 
    
         
            -
                    block.(tmp)
         
     | 
| 
       115 
     | 
    
         
            -
                  end
         
     | 
| 
       116 
     | 
    
         
            -
                end
         
     | 
| 
       117 
     | 
    
         
            -
              rescue RetryWithoutStreaming
         
     | 
| 
       118 
     | 
    
         
            -
                @streaming = false
         
     | 
| 
       119 
     | 
    
         
            -
                retry
         
     | 
| 
       120 
     | 
    
         
            -
              rescue => e
         
     | 
| 
       121 
     | 
    
         
            -
                STDERR.puts "Cannot get #{url.to_s.inspect} (#{e}): #{response&.status_line || 'n/a'}"
         
     | 
| 
       122 
     | 
    
         
            -
                if @debug && !e.is_a?(RuntimeError)
         
     | 
| 
       123 
     | 
    
         
            -
                  STDERR.puts "#{e.backtrace * ?\n}"
         
     | 
| 
       124 
     | 
    
         
            -
                end
         
     | 
| 
       125 
     | 
    
         
            -
                yield HeaderExtension.failed
         
     | 
| 
       126 
     | 
    
         
            -
              end
         
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
              def headers
         
     | 
| 
       129 
     | 
    
         
            -
                {
         
     | 
| 
       130 
     | 
    
         
            -
                  'User-Agent' => Ollama::Client.user_agent,
         
     | 
| 
       131 
     | 
    
         
            -
                }
         
     | 
| 
       132 
     | 
    
         
            -
              end
         
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
              def middlewares
         
     | 
| 
       135 
     | 
    
         
            -
                (Excon.defaults[:middlewares] + [ Excon::Middleware::RedirectFollower ]).uniq
         
     | 
| 
       136 
     | 
    
         
            -
              end
         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
              private
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
              def decorate_io(tmp, response)
         
     | 
| 
       141 
     | 
    
         
            -
                tmp.rewind
         
     | 
| 
       142 
     | 
    
         
            -
                tmp.extend(HeaderExtension)
         
     | 
| 
       143 
     | 
    
         
            -
                if content_type = MIME::Types[response.headers['content-type']].first
         
     | 
| 
       144 
     | 
    
         
            -
                  tmp.content_type = content_type
         
     | 
| 
       145 
     | 
    
         
            -
                end
         
     | 
| 
       146 
     | 
    
         
            -
                if cache_control = response.headers['cache-control'] and
         
     | 
| 
       147 
     | 
    
         
            -
                    cache_control !~ /no-store|no-cache/ and
         
     | 
| 
       148 
     | 
    
         
            -
                    ex = cache_control[/s-maxage\s*=\s*(\d+)/, 1] || cache_control[/max-age\s*=\s*(\d+)/, 1]
         
     | 
| 
       149 
     | 
    
         
            -
                then
         
     | 
| 
       150 
     | 
    
         
            -
                  tmp.ex = ex.to_i
         
     | 
| 
       151 
     | 
    
         
            -
                end
         
     | 
| 
       152 
     | 
    
         
            -
              end
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
              def callback(tmp)
         
     | 
| 
       155 
     | 
    
         
            -
                -> chunk, remaining_bytes, total_bytes do
         
     | 
| 
       156 
     | 
    
         
            -
                  total   = total_bytes or next
         
     | 
| 
       157 
     | 
    
         
            -
                  current = total_bytes - remaining_bytes
         
     | 
| 
       158 
     | 
    
         
            -
                  if @started
         
     | 
| 
       159 
     | 
    
         
            -
                    infobar.counter.progress(by: total - current)
         
     | 
| 
       160 
     | 
    
         
            -
                  else
         
     | 
| 
       161 
     | 
    
         
            -
                    @started = true
         
     | 
| 
       162 
     | 
    
         
            -
                    infobar.counter.reset(total:, current:)
         
     | 
| 
       163 
     | 
    
         
            -
                  end
         
     | 
| 
       164 
     | 
    
         
            -
                  infobar.update(message: message(current, total), force: true)
         
     | 
| 
       165 
     | 
    
         
            -
                  tmp.print(chunk)
         
     | 
| 
       166 
     | 
    
         
            -
                end
         
     | 
| 
       167 
     | 
    
         
            -
              end
         
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
              def message(current, total)
         
     | 
| 
       170 
     | 
    
         
            -
                progress = '%s/%s' % [ current, total ].map {
         
     | 
| 
       171 
     | 
    
         
            -
                  Tins::Unit.format(_1, format: '%.2f %U')
         
     | 
| 
       172 
     | 
    
         
            -
                }
         
     | 
| 
       173 
     | 
    
         
            -
                '%l ' + progress + ' in %te, ETA %e @%E'
         
     | 
| 
       174 
     | 
    
         
            -
              end
         
     | 
| 
       175 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,34 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module Ollama::Utils::FileArgument
         
     | 
| 
       2 
     | 
    
         
            -
              module_function
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
              # Returns the contents of a file or string, or a default value if neither is provided.
         
     | 
| 
       5 
     | 
    
         
            -
              #
         
     | 
| 
       6 
     | 
    
         
            -
              # @param [String] path_or_content The path to a file or a string containing
         
     | 
| 
       7 
     | 
    
         
            -
              #                 the content.
         
     | 
| 
       8 
     | 
    
         
            -
              #
         
     | 
| 
       9 
     | 
    
         
            -
              # @param [String] default The default value to return if no valid input is
         
     | 
| 
       10 
     | 
    
         
            -
              #                 given. Defaults to nil.
         
     | 
| 
       11 
     | 
    
         
            -
              #
         
     | 
| 
       12 
     | 
    
         
            -
              # @return [String] The contents of the file, the string, or the default value.
         
     | 
| 
       13 
     | 
    
         
            -
              #
         
     | 
| 
       14 
     | 
    
         
            -
              # @example Get the contents of a file
         
     | 
| 
       15 
     | 
    
         
            -
              #   get_file_argument('path/to/file')
         
     | 
| 
       16 
     | 
    
         
            -
              #
         
     | 
| 
       17 
     | 
    
         
            -
              # @example Use a string as content
         
     | 
| 
       18 
     | 
    
         
            -
              #   get_file_argument('string content')
         
     | 
| 
       19 
     | 
    
         
            -
              #
         
     | 
| 
       20 
     | 
    
         
            -
              # @example Return a default value if no valid input is given
         
     | 
| 
       21 
     | 
    
         
            -
              #   get_file_argument(nil, default: 'default content')
         
     | 
| 
       22 
     | 
    
         
            -
              def get_file_argument(path_or_content, default: nil)
         
     | 
| 
       23 
     | 
    
         
            -
                if path_or_content.present? && path_or_content.size < 2 ** 15 &&
         
     | 
| 
       24 
     | 
    
         
            -
                    File.basename(path_or_content).size < 2 ** 8 &&
         
     | 
| 
       25 
     | 
    
         
            -
                    File.exist?(path_or_content)
         
     | 
| 
       26 
     | 
    
         
            -
                then
         
     | 
| 
       27 
     | 
    
         
            -
                  File.read(path_or_content)
         
     | 
| 
       28 
     | 
    
         
            -
                elsif path_or_content.present?
         
     | 
| 
       29 
     | 
    
         
            -
                  path_or_content
         
     | 
| 
       30 
     | 
    
         
            -
                else
         
     | 
| 
       31 
     | 
    
         
            -
                  default
         
     | 
| 
       32 
     | 
    
         
            -
                end
         
     | 
| 
       33 
     | 
    
         
            -
              end
         
     | 
| 
       34 
     | 
    
         
            -
            end
         
     | 
    
        data/spec/assets/prompt.txt
    DELETED
    
    | 
         @@ -1 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            You are a test prompt just used for testing.
         
     | 
| 
         @@ -1,43 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            RSpec.describe Ollama::Utils::CacheFetcher do
         
     | 
| 
       4 
     | 
    
         
            -
              let :url do
         
     | 
| 
       5 
     | 
    
         
            -
                'https://www.example.com/hello'
         
     | 
| 
       6 
     | 
    
         
            -
              end
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
              let :cache do
         
     | 
| 
       9 
     | 
    
         
            -
                double('RedisCache')
         
     | 
| 
       10 
     | 
    
         
            -
              end
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
              let :fetcher do
         
     | 
| 
       13 
     | 
    
         
            -
                described_class.new(cache).expose
         
     | 
| 
       14 
     | 
    
         
            -
              end
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
              it 'can be instantiated' do
         
     | 
| 
       17 
     | 
    
         
            -
                expect(fetcher).to be_a described_class
         
     | 
| 
       18 
     | 
    
         
            -
              end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
              it 'has #get' do
         
     | 
| 
       21 
     | 
    
         
            -
                expect(cache).to receive(:[]).with('body-69ce405ab83f42dffa9fd22bbd47783f').and_return 'world'
         
     | 
| 
       22 
     | 
    
         
            -
                expect(cache).to receive(:[]).with('content_type-69ce405ab83f42dffa9fd22bbd47783f').and_return 'text/plain'
         
     | 
| 
       23 
     | 
    
         
            -
                yielded_io = nil
         
     | 
| 
       24 
     | 
    
         
            -
                block = -> io { yielded_io = io }
         
     | 
| 
       25 
     | 
    
         
            -
                fetcher.get(url, &block)
         
     | 
| 
       26 
     | 
    
         
            -
                expect(yielded_io).to be_a StringIO
         
     | 
| 
       27 
     | 
    
         
            -
                expect(yielded_io.read).to eq 'world'
         
     | 
| 
       28 
     | 
    
         
            -
              end
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
              it '#get needs block' do
         
     | 
| 
       31 
     | 
    
         
            -
                expect { fetcher.get(url) }.to raise_error(ArgumentError)
         
     | 
| 
       32 
     | 
    
         
            -
              end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
              it 'has #put' do
         
     | 
| 
       35 
     | 
    
         
            -
                io = StringIO.new('world')
         
     | 
| 
       36 
     | 
    
         
            -
                io.extend(Ollama::Utils::Fetcher::HeaderExtension)
         
     | 
| 
       37 
     | 
    
         
            -
                io.content_type = MIME::Types['text/plain'].first
         
     | 
| 
       38 
     | 
    
         
            -
                io.ex = 666
         
     | 
| 
       39 
     | 
    
         
            -
                expect(cache).to receive(:set).with('body-69ce405ab83f42dffa9fd22bbd47783f', 'world', ex: 666)
         
     | 
| 
       40 
     | 
    
         
            -
                expect(cache).to receive(:set).with('content_type-69ce405ab83f42dffa9fd22bbd47783f', 'text/plain', ex: 666)
         
     | 
| 
       41 
     | 
    
         
            -
                fetcher.put(url, io)
         
     | 
| 
       42 
     | 
    
         
            -
              end
         
     | 
| 
       43 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,137 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            RSpec.describe Ollama::Utils::Fetcher do
         
     | 
| 
       4 
     | 
    
         
            -
              let :url do
         
     | 
| 
       5 
     | 
    
         
            -
                'https://www.example.com/hello'
         
     | 
| 
       6 
     | 
    
         
            -
              end
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
              let :fetcher do
         
     | 
| 
       9 
     | 
    
         
            -
                described_class.new.expose
         
     | 
| 
       10 
     | 
    
         
            -
              end
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
              it 'can be instantiated' do
         
     | 
| 
       13 
     | 
    
         
            -
                expect(fetcher).to be_a described_class
         
     | 
| 
       14 
     | 
    
         
            -
              end
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
              it 'has .get' do
         
     | 
| 
       17 
     | 
    
         
            -
                expect(described_class).to receive(:new).and_return double(get: true)
         
     | 
| 
       18 
     | 
    
         
            -
                described_class.get(url)
         
     | 
| 
       19 
     | 
    
         
            -
              end
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
              it 'can #get with streaming' do
         
     | 
| 
       22 
     | 
    
         
            -
                stub_request(:get, url).
         
     | 
| 
       23 
     | 
    
         
            -
                  with(headers: fetcher.headers).
         
     | 
| 
       24 
     | 
    
         
            -
                  to_return(
         
     | 
| 
       25 
     | 
    
         
            -
                    status: 200,
         
     | 
| 
       26 
     | 
    
         
            -
                    body: 'world',
         
     | 
| 
       27 
     | 
    
         
            -
                    headers: { 'Content-Type' => 'text/plain' },
         
     | 
| 
       28 
     | 
    
         
            -
                  )
         
     | 
| 
       29 
     | 
    
         
            -
                fetcher.get(url) do |tmp|
         
     | 
| 
       30 
     | 
    
         
            -
                  expect(tmp).to be_a Tempfile
         
     | 
| 
       31 
     | 
    
         
            -
                  expect(tmp.read).to eq 'world'
         
     | 
| 
       32 
     | 
    
         
            -
                  expect(tmp.content_type).to eq 'text/plain'
         
     | 
| 
       33 
     | 
    
         
            -
                end
         
     | 
| 
       34 
     | 
    
         
            -
              end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
              it 'can #get without ssl peer verification' do
         
     | 
| 
       37 
     | 
    
         
            -
                fetcher = described_class.new(
         
     | 
| 
       38 
     | 
    
         
            -
                  http_options: { ssl_verify_peer: false }
         
     | 
| 
       39 
     | 
    
         
            -
                ).expose
         
     | 
| 
       40 
     | 
    
         
            -
                stub_request(:get, url).
         
     | 
| 
       41 
     | 
    
         
            -
                  with(headers: fetcher.headers).
         
     | 
| 
       42 
     | 
    
         
            -
                  to_return(
         
     | 
| 
       43 
     | 
    
         
            -
                    status: 200,
         
     | 
| 
       44 
     | 
    
         
            -
                    body: 'world',
         
     | 
| 
       45 
     | 
    
         
            -
                    headers: { 'Content-Type' => 'text/plain' },
         
     | 
| 
       46 
     | 
    
         
            -
                  )
         
     | 
| 
       47 
     | 
    
         
            -
                expect(Excon).to receive(:new).with(
         
     | 
| 
       48 
     | 
    
         
            -
                  url,
         
     | 
| 
       49 
     | 
    
         
            -
                  hash_including(ssl_verify_peer: false)
         
     | 
| 
       50 
     | 
    
         
            -
                ).and_call_original
         
     | 
| 
       51 
     | 
    
         
            -
                fetcher.get(url) do |tmp|
         
     | 
| 
       52 
     | 
    
         
            -
                  expect(tmp).to be_a Tempfile
         
     | 
| 
       53 
     | 
    
         
            -
                  expect(tmp.read).to eq 'world'
         
     | 
| 
       54 
     | 
    
         
            -
                  expect(tmp.content_type).to eq 'text/plain'
         
     | 
| 
       55 
     | 
    
         
            -
                end
         
     | 
| 
       56 
     | 
    
         
            -
              end
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
              it 'can #get and fallback from streaming' do
         
     | 
| 
       59 
     | 
    
         
            -
                stub_request(:get, url).
         
     | 
| 
       60 
     | 
    
         
            -
                  with(headers: fetcher.headers).
         
     | 
| 
       61 
     | 
    
         
            -
                  to_return(
         
     | 
| 
       62 
     | 
    
         
            -
                    { status: 501 },
         
     | 
| 
       63 
     | 
    
         
            -
                    {
         
     | 
| 
       64 
     | 
    
         
            -
                      status: 200,
         
     | 
| 
       65 
     | 
    
         
            -
                      body: 'world',
         
     | 
| 
       66 
     | 
    
         
            -
                      headers: { 'Content-Type' => 'text/plain' },
         
     | 
| 
       67 
     | 
    
         
            -
                    }
         
     | 
| 
       68 
     | 
    
         
            -
                  )
         
     | 
| 
       69 
     | 
    
         
            -
                fetcher.get(url) do |tmp|
         
     | 
| 
       70 
     | 
    
         
            -
                  expect(tmp).to be_a Tempfile
         
     | 
| 
       71 
     | 
    
         
            -
                  expect(tmp.read).to eq 'world'
         
     | 
| 
       72 
     | 
    
         
            -
                  expect(tmp.content_type).to eq 'text/plain'
         
     | 
| 
       73 
     | 
    
         
            -
                end
         
     | 
| 
       74 
     | 
    
         
            -
              end
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
              it 'can #get and finally fail' do
         
     | 
| 
       77 
     | 
    
         
            -
                stub_request(:get, url).
         
     | 
| 
       78 
     | 
    
         
            -
                  with(headers: fetcher.headers).
         
     | 
| 
       79 
     | 
    
         
            -
                  to_return(status: 500)
         
     | 
| 
       80 
     | 
    
         
            -
                expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
         
     | 
| 
       81 
     | 
    
         
            -
                  fetcher.get(url) do |tmp|
         
     | 
| 
       82 
     | 
    
         
            -
                    expect(tmp).to be_a StringIO
         
     | 
| 
       83 
     | 
    
         
            -
                    expect(tmp.read).to eq ''
         
     | 
| 
       84 
     | 
    
         
            -
                    expect(tmp.content_type).to eq 'text/plain'
         
     | 
| 
       85 
     | 
    
         
            -
                  end
         
     | 
| 
       86 
     | 
    
         
            -
              end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
              it 'can redirect' do
         
     | 
| 
       89 
     | 
    
         
            -
                expect(fetcher.middlewares).to include Excon::Middleware::RedirectFollower
         
     | 
| 
       90 
     | 
    
         
            -
              end
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
              it 'can .read' do
         
     | 
| 
       93 
     | 
    
         
            -
                described_class.read(__FILE__) do |file|
         
     | 
| 
       94 
     | 
    
         
            -
                  expect(file).to be_a File
         
     | 
| 
       95 
     | 
    
         
            -
                  expect(file.read).to include 'can .read'
         
     | 
| 
       96 
     | 
    
         
            -
                  expect(file.content_type).to eq 'application/x-ruby'
         
     | 
| 
       97 
     | 
    
         
            -
                end
         
     | 
| 
       98 
     | 
    
         
            -
              end
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
              it 'can .execute' do
         
     | 
| 
       101 
     | 
    
         
            -
                described_class.execute('echo -n hello world') do |file|
         
     | 
| 
       102 
     | 
    
         
            -
                  expect(file).to be_a Tempfile
         
     | 
| 
       103 
     | 
    
         
            -
                  expect(file.read).to eq 'hello world'
         
     | 
| 
       104 
     | 
    
         
            -
                  expect(file.content_type).to eq 'text/plain'
         
     | 
| 
       105 
     | 
    
         
            -
                end
         
     | 
| 
       106 
     | 
    
         
            -
              end
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
              it 'can .execute and fail' do
         
     | 
| 
       109 
     | 
    
         
            -
                expect(IO).to receive(:popen).and_raise StandardError
         
     | 
| 
       110 
     | 
    
         
            -
                expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
         
     | 
| 
       111 
     | 
    
         
            -
                described_class.execute('foobar') do |file|
         
     | 
| 
       112 
     | 
    
         
            -
                  expect(file).to be_a StringIO
         
     | 
| 
       113 
     | 
    
         
            -
                  expect(file.read).to be_empty
         
     | 
| 
       114 
     | 
    
         
            -
                  expect(file.content_type).to eq 'text/plain'
         
     | 
| 
       115 
     | 
    
         
            -
                end
         
     | 
| 
       116 
     | 
    
         
            -
              end
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
              describe '.normalize_url' do
         
     | 
| 
       119 
     | 
    
         
            -
                it 'can handle umlauts' do
         
     | 
| 
       120 
     | 
    
         
            -
                  expect(described_class.normalize_url('https://foo.de/bär')).to eq(
         
     | 
| 
       121 
     | 
    
         
            -
                    'https://foo.de/b%C3%A4r'
         
     | 
| 
       122 
     | 
    
         
            -
                  )
         
     | 
| 
       123 
     | 
    
         
            -
                end
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
                it 'can handle escaped umlauts' do
         
     | 
| 
       126 
     | 
    
         
            -
                  expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
         
     | 
| 
       127 
     | 
    
         
            -
                    'https://foo.de/b%C3%A4r'
         
     | 
| 
       128 
     | 
    
         
            -
                  )
         
     | 
| 
       129 
     | 
    
         
            -
                end
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
                it 'can remove #anchors' do
         
     | 
| 
       132 
     | 
    
         
            -
                  expect(described_class.normalize_url('https://foo.de#bar')).to eq(
         
     | 
| 
       133 
     | 
    
         
            -
                    'https://foo.de'
         
     | 
| 
       134 
     | 
    
         
            -
                  )
         
     | 
| 
       135 
     | 
    
         
            -
                end
         
     | 
| 
       136 
     | 
    
         
            -
              end
         
     | 
| 
       137 
     | 
    
         
            -
            end
         
     | 
| 
         @@ -1,17 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            RSpec.describe Ollama::Utils::FileArgument do
         
     | 
| 
       4 
     | 
    
         
            -
              it 'it can return content' do
         
     | 
| 
       5 
     | 
    
         
            -
                expect(described_class.get_file_argument('foo')).to eq 'foo'
         
     | 
| 
       6 
     | 
    
         
            -
              end
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
              it 'it can return content at path' do
         
     | 
| 
       9 
     | 
    
         
            -
                expect(described_class.get_file_argument(asset('prompt.txt'))).to include\
         
     | 
| 
       10 
     | 
    
         
            -
                  'test prompt'
         
     | 
| 
       11 
     | 
    
         
            -
              end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
              it 'it can return default content' do
         
     | 
| 
       14 
     | 
    
         
            -
                expect(described_class.get_file_argument('', default: 'foo')).to eq 'foo'
         
     | 
| 
       15 
     | 
    
         
            -
                expect(described_class.get_file_argument(nil, default: 'foo')).to eq 'foo'
         
     | 
| 
       16 
     | 
    
         
            -
              end
         
     | 
| 
       17 
     | 
    
         
            -
            end
         
     |