ollama-ruby 0.6.0 → 0.7.0
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 +84 -0
- data/README.md +6 -3
- data/Rakefile +1 -1
- data/bin/ollama_chat +142 -56
- data/bin/ollama_cli +3 -3
- data/bin/ollama_update +2 -0
- data/lib/ollama/client.rb +0 -3
- data/lib/ollama/documents/cache/redis_cache.rb +11 -1
- data/lib/ollama/documents.rb +11 -5
- data/lib/ollama/image.rb +3 -1
- data/lib/ollama/utils/cache_fetcher.rb +3 -3
- data/lib/ollama/utils/chooser.rb +9 -3
- data/lib/ollama/utils/fetcher.rb +18 -8
- data/lib/ollama/utils/tags.rb +60 -6
- data/lib/ollama/utils/width.rb +5 -3
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama.rb +4 -0
- data/ollama-ruby.gemspec +6 -6
- data/spec/ollama/image_spec.rb +5 -0
- data/spec/ollama/utils/cache_fetcher_spec.rb +4 -3
- data/spec/ollama/utils/tags_spec.rb +26 -2
- data/spec/ollama/utils/width_spec.rb +82 -0
- metadata +18 -16
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 5bdf85f296b631c52b44984eee6a5e6d98c82a4cfc4a5764529f4d46b3bdaba7
         | 
| 4 | 
            +
              data.tar.gz: 05e391e492f85f8c6fb1106d49ff4118c26a0922c5978156e2f1364aad82299d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 7a3f77296a526154ed46860a675515b864baa8c991933e76ceeb178b00037df844f20201ad200cb9df8d4fa105066fb40bb0248db3bb65c5bad81012ce3b7288
         | 
| 7 | 
            +
              data.tar.gz: 1d25c9420fba86d992b77f29e4f4f57734dcbdaba864650f626af1c828262a9470f9c981da602335f8d3a03ef3cfc9773ad33d90d3e88a70a4d86be0518d762a
         | 
    
        data/CHANGES.md
    CHANGED
    
    | @@ -1,5 +1,89 @@ | |
| 1 1 | 
             
            # Changes
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 2024-10-03 v0.7.0
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * **Refactor command line interface**
         | 
| 6 | 
            +
            	+ Moved case order
         | 
| 7 | 
            +
            	+ Renamed `/collection clear [tag]|change` to `/collection (clear|change)`
         | 
| 8 | 
            +
            	+ Improved help message, added /info
         | 
| 9 | 
            +
            * **Update README.md** 
         | 
| 10 | 
            +
            	+ Update README.md to reflect changed/added commands
         | 
| 11 | 
            +
            * **Add support for reading PostScript**
         | 
| 12 | 
            +
            	+ Extracted `pdf_read` method to read PDF files using `PDF::Reader`
         | 
| 13 | 
            +
            	+ Added `ps_read` method to read PostScript files by converting them to PDF with Ghostscript and using `pdf_read`.
         | 
| 14 | 
            +
            	+ Updated `parse_source` method to handle PostScript files
         | 
| 15 | 
            +
            * **Update read buffer size for tempfile writes**
         | 
| 16 | 
            +
            	+ Updated `tmp.write` to use a larger buffer size (**16KB**) in IO.popen block.
         | 
| 17 | 
            +
            * **Refactor Collection Chooser and usages**
         | 
| 18 | 
            +
            	+ Added confirmation prompt before clearing collection
         | 
| 19 | 
            +
            	+ Improved collection chooser with `[EXIT]` and `[ALL]` options
         | 
| 20 | 
            +
            	+ Added `ask?` method for user input
         | 
| 21 | 
            +
            * **Add prompt to choose method**
         | 
| 22 | 
            +
            	+ Added `prompt` parameter to `choose` method in `Ollama::Utils::Chooser`
         | 
| 23 | 
            +
            	+ Modified output formatting for selected entry in `choose` method
         | 
| 24 | 
            +
            	+ Updated `choose` method to handle cases better where no entry was chosen
         | 
| 25 | 
            +
            * **Fix Redis cache expiration logic**
         | 
| 26 | 
            +
            	+ Update `set` method to delete key expiration time is less than 1 second.
         | 
| 27 | 
            +
            * **Update dependencies and add source tracking** 
         | 
| 28 | 
            +
            	- Remove `sorted_set` dependency from Rakefile
         | 
| 29 | 
            +
            	- Modify `Ollama::Documents` class to track source of tags
         | 
| 30 | 
            +
            	- Update `Ollama::Utils::Tags` class to include source in tag output and add methods for tracking source
         | 
| 31 | 
            +
            	- Update tests for `Ollama::Utils::Tags` class
         | 
| 32 | 
            +
            * **Refactor width calculation and add tests for wrap and truncate methods.**
         | 
| 33 | 
            +
            	+ Extend `Term::ANSIColor` in `Ollama::Utils::Width`
         | 
| 34 | 
            +
            	+ Update `width` method to use ellipsis length when truncating text
         | 
| 35 | 
            +
            	+ Add tests for `wrap` and `truncate` methods with percentage and length arguments
         | 
| 36 | 
            +
            * **Add attr_reader for data and update equality check**
         | 
| 37 | 
            +
            	+ Added `attr_reader :data` to Ollama::Image class
         | 
| 38 | 
            +
            	+ Updated `==` method in Ollama::Image class to use `other.data`
         | 
| 39 | 
            +
            	+ Added test case in `image_spec.rb` to verify equality of images
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ## 2024-09-30 v0.6.0
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            ### Significant Changes
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            * **Added voice toggle and change functionality**:
         | 
| 46 | 
            +
            	+ Removed `-v` command line switch
         | 
| 47 | 
            +
            	+ Added new Switch class for voice output
         | 
| 48 | 
            +
            	+ Added new method `change_voice` to toggle or change voice output
         | 
| 49 | 
            +
            	+ Updated `info` method to display current voice output if enabled
         | 
| 50 | 
            +
            	+ Updated `display_chat_help` method to include /voice command
         | 
| 51 | 
            +
            * **Added expiring cache support**:
         | 
| 52 | 
            +
            	+ Added `Ollama::Utils::CacheFetcher` class for caching HTTP responses
         | 
| 53 | 
            +
            	+ Modified `Ollama::Utils::Fetcher` to use the new cache class
         | 
| 54 | 
            +
            	+ Updated `ollama_chat` script to use the cache when fetching sources
         | 
| 55 | 
            +
            	+ Added specs for the new cache fetcher class
         | 
| 56 | 
            +
            * **Added change system prompt feature**:
         | 
| 57 | 
            +
            	+ Added `/system` command to change system prompt
         | 
| 58 | 
            +
            	+ Implemented `set_system_prompt` and `change_system_prompt` methods in `bin/ollama_chat`
         | 
| 59 | 
            +
            	+ Updated help messages in `README.md`
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            ### Other Changes
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            * **Updated dependencies**:
         | 
| 64 | 
            +
            	+ Updated version of `xdg` gem to **7.0**
         | 
| 65 | 
            +
            	+ Added `xdg` dependency to Rakefile
         | 
| 66 | 
            +
            * **Refactored error handling**:
         | 
| 67 | 
            +
            	+ Warn message updated to include more context about the error
         | 
| 68 | 
            +
            	+ `warn` statement now mentions "while pulling model"
         | 
| 69 | 
            +
            * **Updated chat commands and added clipboard functionality**:
         | 
| 70 | 
            +
            	+ Added `/copy` command to copy last response to clipboard
         | 
| 71 | 
            +
            	+ Implemented `copy_to_clipboard` method in `ollama_chat`
         | 
| 72 | 
            +
            	+ Updated chat help display to include new `/copy` command
         | 
| 73 | 
            +
            * **Refactored Ollama::Utils::Fetcher**:
         | 
| 74 | 
            +
            	+ Made instance methods private and only exposed class methods
         | 
| 75 | 
            +
            	+ Added `expose` method to `Ollama::Utils::FetcherSpec` for testing
         | 
| 76 | 
            +
            * **Added version command to ollama chat binary**:
         | 
| 77 | 
            +
            	+ Added `version` method to print Ollama version and exit
         | 
| 78 | 
            +
            	+ Updated `$opts` string in `ollama` script to include `-V` option for version command
         | 
| 79 | 
            +
            	+ Added call to `version` method when `-V` option is used
         | 
| 80 | 
            +
            * **Updated system prompt display**:
         | 
| 81 | 
            +
            	+ Changed `Ollama::Utils::Width.wrap` to `Ollama::Utils::ANSIMarkdown.parse` in `show_system_prompt` method
         | 
| 82 | 
            +
            * **Added system prompt configuration via search_ui for ? argument value**:
         | 
| 83 | 
            +
            	+ Added `show_system_prompt` method to print configured system prompt
         | 
| 84 | 
            +
            	+ Modified `info` method to include system prompt in output
         | 
| 85 | 
            +
            	+ Implemented option `-s ?` to choose or specify system prompt
         | 
| 86 | 
            +
             | 
| 3 87 | 
             
            ## 2024-09-26 v0.5.0
         | 
| 4 88 |  | 
| 5 89 | 
             
            ### New Features
         | 
    
        data/README.md
    CHANGED
    
    | @@ -34,7 +34,7 @@ This a chat client, that can be used to connect to an ollama server and enter a | |
| 34 34 | 
             
            chat converstation with a LLM. It can be called with the following arguments:
         | 
| 35 35 |  | 
| 36 36 | 
             
            ```
         | 
| 37 | 
            -
            ollama_chat [OPTIONS]
         | 
| 37 | 
            +
            Usage: ollama_chat [OPTIONS]
         | 
| 38 38 |  | 
| 39 39 | 
             
              -f CONFIG      config file to read
         | 
| 40 40 | 
             
              -u URL         the ollama base url, OLLAMA_URL
         | 
| @@ -42,9 +42,10 @@ ollama_chat [OPTIONS] | |
| 42 42 | 
             
              -s SYSTEM      the system prompt to use as a file, OLLAMA_CHAT_SYSTEM
         | 
| 43 43 | 
             
              -c CHAT        a saved chat conversation to load
         | 
| 44 44 | 
             
              -C COLLECTION  name of the collection used in this conversation
         | 
| 45 | 
            -
              -D DOCUMENT    load document and add to collection (multiple)
         | 
| 45 | 
            +
              -D DOCUMENT    load document and add to embeddings collection (multiple)
         | 
| 46 46 | 
             
              -M             use (empty) MemoryCache for this chat session
         | 
| 47 47 | 
             
              -E             disable embeddings for this chat session
         | 
| 48 | 
            +
              -V             display the current version number and quit
         | 
| 48 49 | 
             
              -h             this help
         | 
| 49 50 | 
             
            ```
         | 
| 50 51 |  | 
| @@ -157,6 +158,7 @@ The following commands can be given inside the chat, if prefixed by a `/`: | |
| 157 158 | 
             
            /paste                          to paste content
         | 
| 158 159 | 
             
            /markdown                       toggle markdown output
         | 
| 159 160 | 
             
            /stream                         toggle stream output
         | 
| 161 | 
            +
            /location                       toggle location submission
         | 
| 160 162 | 
             
            /voice( change)                 toggle voice output or change the voice
         | 
| 161 163 | 
             
            /list [n]                       list the last n / all conversation exchanges
         | 
| 162 164 | 
             
            /clear                          clear the whole conversation
         | 
| @@ -165,7 +167,8 @@ The following commands can be given inside the chat, if prefixed by a `/`: | |
| 165 167 | 
             
            /model                          change the model
         | 
| 166 168 | 
             
            /system                         change system prompt (clears conversation)
         | 
| 167 169 | 
             
            /regenerate                     the last answer message
         | 
| 168 | 
            -
            /collection clear | 
| 170 | 
            +
            /collection( clear|change)      change (default) collection or clear
         | 
| 171 | 
            +
            /info                           show information for current session
         | 
| 169 172 | 
             
            /import source                  import the source's content
         | 
| 170 173 | 
             
            /summarize [n] source           summarize the source's content in n words
         | 
| 171 174 | 
             
            /embedding                      toggle embedding paused or not
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -32,7 +32,6 @@ GemHadar do | |
| 32 32 | 
             
              dependency             'redis',                 '~> 5.0'
         | 
| 33 33 | 
             
              dependency             'numo-narray',           '~> 0.9'
         | 
| 34 34 | 
             
              dependency             'more_math',             '~> 1.1'
         | 
| 35 | 
            -
              dependency             'sorted_set',            '~> 1.0'
         | 
| 36 35 | 
             
              dependency             'mime-types',            '~> 3.0'
         | 
| 37 36 | 
             
              dependency             'reverse_markdown',      '~> 2.0'
         | 
| 38 37 | 
             
              dependency             'complex_config',        '~> 0.22'
         | 
| @@ -42,6 +41,7 @@ GemHadar do | |
| 42 41 | 
             
              dependency             'logger',                '~> 1.0'
         | 
| 43 42 | 
             
              dependency             'json',                  '~> 2.0'
         | 
| 44 43 | 
             
              dependency             'xdg',                   '~> 7.0'
         | 
| 44 | 
            +
              dependency             'tins',                  '~> 1.34'
         | 
| 45 45 | 
             
              development_dependency 'all_images',            '~> 0.4'
         | 
| 46 46 | 
             
              development_dependency 'rspec',                 '~> 3.2'
         | 
| 47 47 | 
             
              development_dependency 'webmock'
         | 
    
        data/bin/ollama_chat
    CHANGED
    
    | @@ -4,9 +4,6 @@ require 'ollama' | |
| 4 4 | 
             
            include Ollama
         | 
| 5 5 | 
             
            require 'term/ansicolor'
         | 
| 6 6 | 
             
            include Term::ANSIColor
         | 
| 7 | 
            -
            require 'tins'
         | 
| 8 | 
            -
            require 'tins/xt/full'
         | 
| 9 | 
            -
            require 'tins/xt/hash_union'
         | 
| 10 7 | 
             
            include Tins::GO
         | 
| 11 8 | 
             
            require 'reline'
         | 
| 12 9 | 
             
            require 'reverse_markdown'
         | 
| @@ -31,6 +28,11 @@ class OllamaChatConfig | |
| 31 28 | 
             
                  name: <%= ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1') %>
         | 
| 32 29 | 
             
                  options:
         | 
| 33 30 | 
             
                    num_ctx: 8192
         | 
| 31 | 
            +
                location:
         | 
| 32 | 
            +
                  enabled: false
         | 
| 33 | 
            +
                  name: Berlin
         | 
| 34 | 
            +
                  decimal_degrees: [ 52.514127, 13.475211 ]
         | 
| 35 | 
            +
                  units: SI (International System of Units) # or USCS (United States Customary System)
         | 
| 34 36 | 
             
                prompts:
         | 
| 35 37 | 
             
                  embed: "This source was now embedded: %{source}"
         | 
| 36 38 | 
             
                  summarize: |
         | 
| @@ -83,7 +85,7 @@ class OllamaChatConfig | |
| 83 85 | 
             
                if @filename == default_path && !retried
         | 
| 84 86 | 
             
                  retried = true
         | 
| 85 87 | 
             
                  mkdir_p File.dirname(default_path)
         | 
| 86 | 
            -
                  File.secure_write(default_path | 
| 88 | 
            +
                  File.secure_write(default_path, DEFAULT_CONFIG)
         | 
| 87 89 | 
             
                  retry
         | 
| 88 90 | 
             
                else
         | 
| 89 91 | 
             
                  raise
         | 
| @@ -178,7 +180,7 @@ end | |
| 178 180 |  | 
| 179 181 | 
             
            class Switch
         | 
| 180 182 | 
             
              def initialize(name, msg:, config: $config)
         | 
| 181 | 
            -
                @value = !!config.send("#{name}?")
         | 
| 183 | 
            +
                @value = [ false, true ].include?(config) ? config : !!config.send("#{name}?")
         | 
| 182 184 | 
             
                @msg   = msg
         | 
| 183 185 | 
             
              end
         | 
| 184 186 |  | 
| @@ -259,9 +261,21 @@ def setup_switches | |
| 259 261 | 
             
                  false => "Embedding is currently not performed.",
         | 
| 260 262 | 
             
                }
         | 
| 261 263 | 
             
              )
         | 
| 264 | 
            +
             | 
| 265 | 
            +
              $location = Switch.new(
         | 
| 266 | 
            +
                :location,
         | 
| 267 | 
            +
                msg: {
         | 
| 268 | 
            +
                  true  => "Location and localtime enabled.",
         | 
| 269 | 
            +
                  false => "Location and localtime disabled.",
         | 
| 270 | 
            +
                },
         | 
| 271 | 
            +
                config: $config.location.enabled
         | 
| 272 | 
            +
              )
         | 
| 262 273 | 
             
            end
         | 
| 263 274 |  | 
| 264 275 | 
             
            def search_web(query, n = nil)
         | 
| 276 | 
            +
              if l = at_location
         | 
| 277 | 
            +
                query += " #{at_location}"
         | 
| 278 | 
            +
              end
         | 
| 265 279 | 
             
              n = n.to_i
         | 
| 266 280 | 
             
              n < 1 and n = 1
         | 
| 267 281 | 
             
              query = URI.encode_uri_component(query)
         | 
| @@ -401,6 +415,32 @@ def parse_atom(source_io) | |
| 401 415 | 
             
              end
         | 
| 402 416 | 
             
            end
         | 
| 403 417 |  | 
| 418 | 
            +
            def pdf_read(io)
         | 
| 419 | 
            +
              reader = PDF::Reader.new(io)
         | 
| 420 | 
            +
              reader.pages.inject(+'') { |result, page| result << page.text }
         | 
| 421 | 
            +
            end
         | 
| 422 | 
            +
             | 
| 423 | 
            +
            def ps_read(io)
         | 
| 424 | 
            +
              gs = `which gs`.chomp
         | 
| 425 | 
            +
              if gs.present?
         | 
| 426 | 
            +
                Tempfile.create do |tmp|
         | 
| 427 | 
            +
                  IO.popen("#{gs} -q -sDEVICE=pdfwrite -sOutputFile=#{tmp.path} -", 'wb') do |gs_io|
         | 
| 428 | 
            +
                    until io.eof?
         | 
| 429 | 
            +
                      buffer = io.read(1 << 17)
         | 
| 430 | 
            +
                      IO.select(nil, [ gs_io ], nil)
         | 
| 431 | 
            +
                      gs_io.write buffer
         | 
| 432 | 
            +
                    end
         | 
| 433 | 
            +
                    gs_io.close
         | 
| 434 | 
            +
                    File.open(tmp.path, 'rb') do |pdf|
         | 
| 435 | 
            +
                      pdf_read(pdf)
         | 
| 436 | 
            +
                    end
         | 
| 437 | 
            +
                  end
         | 
| 438 | 
            +
                end
         | 
| 439 | 
            +
              else
         | 
| 440 | 
            +
                STDERR.puts "Cannot convert #{io&.content_type} whith ghostscript, gs not in path."
         | 
| 441 | 
            +
              end
         | 
| 442 | 
            +
            end
         | 
| 443 | 
            +
             | 
| 404 444 | 
             
            def parse_source(source_io)
         | 
| 405 445 | 
             
              case source_io&.content_type
         | 
| 406 446 | 
             
              when 'text/html'
         | 
| @@ -426,12 +466,11 @@ def parse_source(source_io) | |
| 426 466 | 
             
                parse_rss(source_io)
         | 
| 427 467 | 
             
              when 'application/atom+xml'
         | 
| 428 468 | 
             
                parse_atom(source_io)
         | 
| 429 | 
            -
              when 'application/ | 
| 430 | 
            -
                source_io | 
| 469 | 
            +
              when 'application/postscript'
         | 
| 470 | 
            +
                ps_read(source_io)
         | 
| 431 471 | 
             
              when 'application/pdf'
         | 
| 432 | 
            -
                 | 
| 433 | 
            -
             | 
| 434 | 
            -
              when %r(\Atext/), nil
         | 
| 472 | 
            +
                pdf_read(source_io)
         | 
| 473 | 
            +
              when %r(\Aapplication/(json|ld\+json|x-ruby|x-perl|x-gawk|x-python|x-javascript|x-c?sh|x-dosexec|x-shellscript|x-tex|x-latex|x-lyx|x-bibtex)), %r(\Atext/), nil
         | 
| 435 474 | 
             
                source_io.read
         | 
| 436 475 | 
             
              else
         | 
| 437 476 | 
             
                STDERR.puts "Cannot embed #{source_io&.content_type} document."
         | 
| @@ -478,7 +517,7 @@ def embed_source(source_io, source) | |
| 478 517 | 
             
                  length: 10
         | 
| 479 518 | 
             
                )
         | 
| 480 519 | 
             
              end
         | 
| 481 | 
            -
              $documents.add(inputs, source: | 
| 520 | 
            +
              $documents.add(inputs, source:)
         | 
| 482 521 | 
             
            end
         | 
| 483 522 |  | 
| 484 523 | 
             
            def add_image(images, source_io, source)
         | 
| @@ -576,7 +615,7 @@ def parse_content(content, images) | |
| 576 615 | 
             
              content.scan(%r([.~]?/\S+|https?://\S+|#\S+)).each do |source|
         | 
| 577 616 | 
             
                case source
         | 
| 578 617 | 
             
                when /\A#(\S+)/
         | 
| 579 | 
            -
                  tags | 
| 618 | 
            +
                  tags.add($1, source:)
         | 
| 580 619 | 
             
                else
         | 
| 581 620 | 
             
                  source = source.sub(/(["')]|\*+)\z/, '')
         | 
| 582 621 | 
             
                  fetch_source(source) do |source_io|
         | 
| @@ -598,29 +637,37 @@ def parse_content(content, images) | |
| 598 637 | 
             
              return content, (tags unless tags.empty?)
         | 
| 599 638 | 
             
            end
         | 
| 600 639 |  | 
| 601 | 
            -
            def choose_model(cli_model,  | 
| 640 | 
            +
            def choose_model(cli_model, current_model)
         | 
| 602 641 | 
             
              models = ollama.tags.models.map(&:name).sort
         | 
| 603 642 | 
             
              model = if cli_model == ''
         | 
| 604 | 
            -
                        Ollama::Utils::Chooser.choose(models) ||  | 
| 643 | 
            +
                        Ollama::Utils::Chooser.choose(models) || current_model
         | 
| 605 644 | 
             
                      else
         | 
| 606 | 
            -
                        cli_model ||  | 
| 645 | 
            +
                        cli_model || current_model
         | 
| 607 646 | 
             
                      end
         | 
| 608 647 | 
             
            ensure
         | 
| 609 648 | 
             
              puts green { "Connecting to #{model}@#{ollama.base_url} now…" }
         | 
| 610 649 | 
             
            end
         | 
| 611 650 |  | 
| 612 | 
            -
            def  | 
| 613 | 
            -
               | 
| 651 | 
            +
            def ask?(prompt:)
         | 
| 652 | 
            +
              print prompt
         | 
| 653 | 
            +
              STDIN.gets.chomp
         | 
| 654 | 
            +
            end
         | 
| 655 | 
            +
             | 
| 656 | 
            +
            def choose_collection(current_collection)
         | 
| 657 | 
            +
              collections = [ current_collection ] + $documents.collections
         | 
| 614 658 | 
             
              collections = collections.compact.map(&:to_s).uniq.sort
         | 
| 615 | 
            -
              collections.unshift('[NEW]')
         | 
| 616 | 
            -
              collection = Ollama::Utils::Chooser.choose(collections) ||  | 
| 617 | 
            -
               | 
| 618 | 
            -
             | 
| 619 | 
            -
             | 
| 659 | 
            +
              collections.unshift('[EXIT]').unshift('[NEW]')
         | 
| 660 | 
            +
              collection = Ollama::Utils::Chooser.choose(collections) || current_collection
         | 
| 661 | 
            +
              case collection
         | 
| 662 | 
            +
              when '[NEW]'
         | 
| 663 | 
            +
                $documents.collection = ask?(prompt: "Enter name of the new collection: ")
         | 
| 664 | 
            +
              when nil, '[EXIT]'
         | 
| 665 | 
            +
                puts "Exiting chooser."
         | 
| 666 | 
            +
              when /./
         | 
| 667 | 
            +
                $documents.collection = collection
         | 
| 620 668 | 
             
              end
         | 
| 621 | 
            -
              $documents.collection = collection
         | 
| 622 669 | 
             
            ensure
         | 
| 623 | 
            -
              puts " | 
| 670 | 
            +
              puts "Using collection #{bold{$documents.collection}}."
         | 
| 624 671 | 
             
              collection_stats
         | 
| 625 672 | 
             
            end
         | 
| 626 673 |  | 
| @@ -652,13 +699,25 @@ def show_system_prompt | |
| 652 699 | 
             
              EOT
         | 
| 653 700 | 
             
            end
         | 
| 654 701 |  | 
| 702 | 
            +
            def at_location
         | 
| 703 | 
            +
              if $location.on?
         | 
| 704 | 
            +
                location_name            = $config.location.name
         | 
| 705 | 
            +
                location_decimal_degrees = $config.location.decimal_degrees * ', '
         | 
| 706 | 
            +
                localtime                = Time.now.iso8601
         | 
| 707 | 
            +
                units                    = $config.location.units
         | 
| 708 | 
            +
                $config.prompts.location % {
         | 
| 709 | 
            +
                  location_name:, location_decimal_degrees:, localtime:, units:,
         | 
| 710 | 
            +
                }
         | 
| 711 | 
            +
              end.to_s
         | 
| 712 | 
            +
            end
         | 
| 713 | 
            +
             | 
| 655 714 | 
             
            def set_system_prompt(messages, system)
         | 
| 656 715 | 
             
              $system = system
         | 
| 657 716 | 
             
              messages.clear
         | 
| 658 717 | 
             
              messages << Message.new(role: 'system', content: system)
         | 
| 659 718 | 
             
            end
         | 
| 660 719 |  | 
| 661 | 
            -
            def change_system_prompt(messages)
         | 
| 720 | 
            +
            def change_system_prompt(messages, default)
         | 
| 662 721 | 
             
              prompts = $config.system_prompts.attribute_names.compact
         | 
| 663 722 | 
             
              chosen  = Ollama::Utils::Chooser.choose(prompts)
         | 
| 664 723 | 
             
              system  = if chosen
         | 
| @@ -684,6 +743,7 @@ def info | |
| 684 743 | 
             
              puts "Documents database cache is #{$documents.nil? ? 'n/a' : bold{$documents.cache.class}}"
         | 
| 685 744 | 
             
              $markdown.show
         | 
| 686 745 | 
             
              $stream.show
         | 
| 746 | 
            +
              $location.show
         | 
| 687 747 | 
             
              if $voice.on?
         | 
| 688 748 | 
             
                puts "Using voice #{bold{$current_voice}} to speak."
         | 
| 689 749 | 
             
              end
         | 
| @@ -716,6 +776,7 @@ def display_chat_help | |
| 716 776 | 
             
                /paste                          to paste content
         | 
| 717 777 | 
             
                /markdown                       toggle markdown output
         | 
| 718 778 | 
             
                /stream                         toggle stream output
         | 
| 779 | 
            +
                /location                       toggle location submission
         | 
| 719 780 | 
             
                /voice( change)                 toggle voice output or change the voice
         | 
| 720 781 | 
             
                /list [n]                       list the last n / all conversation exchanges
         | 
| 721 782 | 
             
                /clear                          clear the whole conversation
         | 
| @@ -724,7 +785,8 @@ def display_chat_help | |
| 724 785 | 
             
                /model                          change the model
         | 
| 725 786 | 
             
                /system                         change system prompt (clears conversation)
         | 
| 726 787 | 
             
                /regenerate                     the last answer message
         | 
| 727 | 
            -
                /collection clear | 
| 788 | 
            +
                /collection( clear|change)      change (default) collection or clear
         | 
| 789 | 
            +
                /info                           show information for current session
         | 
| 728 790 | 
             
                /import source                  import the source's content
         | 
| 729 791 | 
             
                /summarize [n] source           summarize the source's content in n words
         | 
| 730 792 | 
             
                /embedding                      toggle embedding paused or not
         | 
| @@ -739,7 +801,7 @@ end | |
| 739 801 |  | 
| 740 802 | 
             
            def usage
         | 
| 741 803 | 
             
              puts <<~EOT
         | 
| 742 | 
            -
                #{File.basename($0)} [OPTIONS]
         | 
| 804 | 
            +
                Usage: #{File.basename($0)} [OPTIONS]
         | 
| 743 805 |  | 
| 744 806 | 
             
                  -f CONFIG      config file to read
         | 
| 745 807 | 
             
                  -u URL         the ollama base url, OLLAMA_URL
         | 
| @@ -790,7 +852,7 @@ if $opts[?c] | |
| 790 852 | 
             
            else
         | 
| 791 853 | 
             
              default = $config.system_prompts.default? || model_system
         | 
| 792 854 | 
             
              if $opts[?s] == ??
         | 
| 793 | 
            -
                change_system_prompt(messages)
         | 
| 855 | 
            +
                change_system_prompt(messages, default)
         | 
| 794 856 | 
             
              else
         | 
| 795 857 | 
             
                system = Ollama::Utils::FileArgument.get_file_argument($opts[?s], default:)
         | 
| 796 858 | 
             
                system.present? and set_system_prompt(messages, system)
         | 
| @@ -860,18 +922,21 @@ loop do | |
| 860 922 | 
             
              content = Reline.readline(input_prompt, true)&.chomp
         | 
| 861 923 |  | 
| 862 924 | 
             
              case content
         | 
| 863 | 
            -
              when %r(^/paste$)
         | 
| 864 | 
            -
                puts bold { "Paste your content and then press C-d!" }
         | 
| 865 | 
            -
                content = STDIN.read
         | 
| 866 925 | 
             
              when %r(^/copy$)
         | 
| 867 926 | 
             
                copy_to_clipboard(messages)
         | 
| 868 927 | 
             
                next
         | 
| 928 | 
            +
              when %r(^/paste$)
         | 
| 929 | 
            +
                puts bold { "Paste your content and then press C-d!" }
         | 
| 930 | 
            +
                content = STDIN.read
         | 
| 869 931 | 
             
              when %r(^/markdown$)
         | 
| 870 932 | 
             
                $markdown.toggle
         | 
| 871 933 | 
             
                next
         | 
| 872 934 | 
             
              when %r(^/stream$)
         | 
| 873 935 | 
             
                $stream.toggle
         | 
| 874 936 | 
             
                next
         | 
| 937 | 
            +
              when %r(^/location$)
         | 
| 938 | 
            +
                $location.toggle
         | 
| 939 | 
            +
                next
         | 
| 875 940 | 
             
              when %r(^/voice(?:\s+(change))?$)
         | 
| 876 941 | 
             
                if $1 == 'change'
         | 
| 877 942 | 
             
                  change_voice
         | 
| @@ -890,33 +955,14 @@ loop do | |
| 890 955 | 
             
                puts "Cleared messages."
         | 
| 891 956 | 
             
                next
         | 
| 892 957 | 
             
              when %r(^/clobber$)
         | 
| 893 | 
            -
                 | 
| 894 | 
            -
             | 
| 895 | 
            -
             | 
| 896 | 
            -
             | 
| 897 | 
            -
             | 
| 898 | 
            -
             | 
| 899 | 
            -
                case command
         | 
| 900 | 
            -
                when 'clear'
         | 
| 901 | 
            -
                  tags = arg.present? ? arg.sub(/\A#*/, '') : nil
         | 
| 902 | 
            -
                  if tags
         | 
| 903 | 
            -
                    $documents.clear(tags:)
         | 
| 904 | 
            -
                    puts "Cleared tag ##{tags} from collection #{bold{collection}}."
         | 
| 905 | 
            -
                  else
         | 
| 906 | 
            -
                    $documents.clear
         | 
| 907 | 
            -
                    puts "Cleared collection #{bold{collection}}."
         | 
| 908 | 
            -
                  end
         | 
| 909 | 
            -
                when 'change'
         | 
| 910 | 
            -
                  choose_collection(collection)
         | 
| 958 | 
            +
                if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
         | 
| 959 | 
            +
                  clear_messages(messages)
         | 
| 960 | 
            +
                  $documents.clear
         | 
| 961 | 
            +
                  puts "Cleared messages and collection #{bold{$documents.collection}}."
         | 
| 962 | 
            +
                else
         | 
| 963 | 
            +
                  puts 'Cancelled.'
         | 
| 911 964 | 
             
                end
         | 
| 912 965 | 
             
                next
         | 
| 913 | 
            -
              when %r(^/system$)
         | 
| 914 | 
            -
                change_system_prompt(messages)
         | 
| 915 | 
            -
                info
         | 
| 916 | 
            -
                next
         | 
| 917 | 
            -
              when %r(/info)
         | 
| 918 | 
            -
                info
         | 
| 919 | 
            -
                next
         | 
| 920 966 | 
             
              when %r(^/pop(?:\s+(\d*))?$)
         | 
| 921 967 | 
             
                if messages.size > 1
         | 
| 922 968 | 
             
                  n = $1.to_i.clamp(1, Float::INFINITY)
         | 
| @@ -931,6 +977,10 @@ loop do | |
| 931 977 | 
             
              when %r(^/model$)
         | 
| 932 978 | 
             
                $model = choose_model('', $model)
         | 
| 933 979 | 
             
                next
         | 
| 980 | 
            +
              when %r(^/system$)
         | 
| 981 | 
            +
                change_system_prompt(messages, $system)
         | 
| 982 | 
            +
                info
         | 
| 983 | 
            +
                next
         | 
| 934 984 | 
             
              when %r(^/regenerate$)
         | 
| 935 985 | 
             
                if content = messages[-2]&.content
         | 
| 936 986 | 
             
                  content.gsub!(/\nConsider these chunks for your answer.*\z/, '')
         | 
| @@ -941,6 +991,38 @@ loop do | |
| 941 991 | 
             
                end
         | 
| 942 992 | 
             
                parse_content = false
         | 
| 943 993 | 
             
                content
         | 
| 994 | 
            +
              when %r(^/collection(?:\s+(clear|change))?$)
         | 
| 995 | 
            +
                case $1 || 'change'
         | 
| 996 | 
            +
                when 'clear'
         | 
| 997 | 
            +
                  loop do
         | 
| 998 | 
            +
                    tags = $documents.tags.add('[EXIT]').add('[ALL]')
         | 
| 999 | 
            +
                    tag = Ollama::Utils::Chooser.choose(tags, prompt: 'Clear? %s')
         | 
| 1000 | 
            +
                    case tag
         | 
| 1001 | 
            +
                    when nil, '[EXIT]'
         | 
| 1002 | 
            +
                      puts "Exiting chooser."
         | 
| 1003 | 
            +
                      break
         | 
| 1004 | 
            +
                    when '[ALL]'
         | 
| 1005 | 
            +
                      if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
         | 
| 1006 | 
            +
                        $documents.clear
         | 
| 1007 | 
            +
                        puts "Cleared collection #{bold{$documents.collection}}."
         | 
| 1008 | 
            +
                        break
         | 
| 1009 | 
            +
                      else
         | 
| 1010 | 
            +
                        puts 'Cancelled.'
         | 
| 1011 | 
            +
                        sleep 3
         | 
| 1012 | 
            +
                      end
         | 
| 1013 | 
            +
                    when /./
         | 
| 1014 | 
            +
                      $documents.clear(tags: [ tag ])
         | 
| 1015 | 
            +
                      puts "Cleared tag #{tag} from collection #{bold{$documents.collection}}."
         | 
| 1016 | 
            +
                      sleep 3
         | 
| 1017 | 
            +
                    end
         | 
| 1018 | 
            +
                  end
         | 
| 1019 | 
            +
                when 'change'
         | 
| 1020 | 
            +
                  choose_collection($documents.collection)
         | 
| 1021 | 
            +
                end
         | 
| 1022 | 
            +
                next
         | 
| 1023 | 
            +
              when %r(/info)
         | 
| 1024 | 
            +
                info
         | 
| 1025 | 
            +
                next
         | 
| 944 1026 | 
             
              when %r(^/import\s+(.+))
         | 
| 945 1027 | 
             
                parse_content = false
         | 
| 946 1028 | 
             
                content       = import($1) or next
         | 
| @@ -1007,6 +1089,10 @@ loop do | |
| 1007 1089 | 
             
                end
         | 
| 1008 1090 | 
             
              end
         | 
| 1009 1091 |  | 
| 1092 | 
            +
              if location = at_location.full?
         | 
| 1093 | 
            +
                content += " [#{location} – do not comment on this information, just consider it for eventual queries]"
         | 
| 1094 | 
            +
              end
         | 
| 1095 | 
            +
             | 
| 1010 1096 | 
             
              messages << Message.new(role: 'user', content:, images: images.dup)
         | 
| 1011 1097 | 
             
              images.clear
         | 
| 1012 1098 | 
             
              handler = FollowChat.new(messages:, markdown: $markdown.on?, voice: ($current_voice if $voice.on?))
         | 
    
        data/bin/ollama_cli
    CHANGED
    
    | @@ -8,8 +8,8 @@ include Tins::GO | |
| 8 8 | 
             
            require 'json'
         | 
| 9 9 |  | 
| 10 10 | 
             
            def usage
         | 
| 11 | 
            -
              puts <<~ | 
| 12 | 
            -
                #{File.basename($0)} [OPTIONS]
         | 
| 11 | 
            +
              puts <<~EOT
         | 
| 12 | 
            +
                Usage: #{File.basename($0)} [OPTIONS]
         | 
| 13 13 |  | 
| 14 14 | 
             
                  -u URL         the ollama base url, OLLAMA_URL
         | 
| 15 15 | 
             
                  -m MODEL       the ollama model to chat with, OLLAMA_MODEL
         | 
| @@ -22,7 +22,7 @@ def usage | |
| 22 22 | 
             
                  -S             use streaming for generation
         | 
| 23 23 | 
             
                  -h             this help
         | 
| 24 24 |  | 
| 25 | 
            -
               | 
| 25 | 
            +
              EOT
         | 
| 26 26 | 
             
              exit 0
         | 
| 27 27 | 
             
            end
         | 
| 28 28 |  | 
    
        data/bin/ollama_update
    CHANGED
    
    | @@ -14,4 +14,6 @@ ollama.tags.models.each do |model| | |
| 14 14 | 
             
                "Updating model #{bold {name}} (last modified at #{modified_at.iso8601}):"
         | 
| 15 15 | 
             
              )
         | 
| 16 16 | 
             
              ollama.pull(name:)
         | 
| 17 | 
            +
            rescue Ollama::Errors::Error => e
         | 
| 18 | 
            +
              infobar.puts "Caught #{e.class} for model #{bold { model.name }}: #{e} => Continuing."
         | 
| 17 19 | 
             
            end
         | 
    
        data/lib/ollama/client.rb
    CHANGED
    
    
| @@ -23,7 +23,17 @@ class Ollama::Documents::RedisCache | |
| 23 23 | 
             
              end
         | 
| 24 24 |  | 
| 25 25 | 
             
              def []=(key, value)
         | 
| 26 | 
            -
                 | 
| 26 | 
            +
                set(key, value)
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def set(key, value, ex: nil)
         | 
| 30 | 
            +
                ex ||= @ex
         | 
| 31 | 
            +
                if !ex.nil? && ex < 1
         | 
| 32 | 
            +
                  redis.del(pre(key))
         | 
| 33 | 
            +
                else
         | 
| 34 | 
            +
                  redis.set(pre(key), JSON.generate(value), ex:)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                value
         | 
| 27 37 | 
             
              end
         | 
| 28 38 |  | 
| 29 39 | 
             
              def ttl(key)
         | 
    
        data/lib/ollama/documents.rb
    CHANGED
    
    | @@ -25,7 +25,7 @@ class Ollama::Documents | |
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                def tags_set
         | 
| 28 | 
            -
                  Ollama::Utils::Tags.new(tags)
         | 
| 28 | 
            +
                  Ollama::Utils::Tags.new(tags, source:)
         | 
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| 31 31 | 
             
                def ==(other)
         | 
| @@ -57,8 +57,10 @@ class Ollama::Documents | |
| 57 57 |  | 
| 58 58 | 
             
              def add(inputs, batch_size: 10, source: nil, tags: [])
         | 
| 59 59 | 
             
                inputs = Array(inputs)
         | 
| 60 | 
            -
                tags | 
| 61 | 
            -
                 | 
| 60 | 
            +
                tags = Ollama::Utils::Tags.new(tags, source:)
         | 
| 61 | 
            +
                if source
         | 
| 62 | 
            +
                  tags.add(File.basename(source).gsub(/\?.*/, ''), source:)
         | 
| 63 | 
            +
                end
         | 
| 62 64 | 
             
                inputs.map! { |i|
         | 
| 63 65 | 
             
                  text = i.respond_to?(:read) ? i.read : i.to_s
         | 
| 64 66 | 
             
                  text
         | 
| @@ -70,7 +72,7 @@ class Ollama::Documents | |
| 70 72 | 
             
                end
         | 
| 71 73 | 
             
                batches = inputs.each_slice(batch_size).
         | 
| 72 74 | 
             
                  with_infobar(
         | 
| 73 | 
            -
                    label: "Add #{truncate(tags.to_s, percentage: 25)}",
         | 
| 75 | 
            +
                    label: "Add #{truncate(tags.to_s(link: false), percentage: 25)}",
         | 
| 74 76 | 
             
                    total: inputs.size
         | 
| 75 77 | 
             
                  )
         | 
| 76 78 | 
             
                batches.each do |batch|
         | 
| @@ -159,7 +161,11 @@ class Ollama::Documents | |
| 159 161 | 
             
              end
         | 
| 160 162 |  | 
| 161 163 | 
             
              def tags
         | 
| 162 | 
            -
                @cache. | 
| 164 | 
            +
                @cache.each_with_object(Ollama::Utils::Tags.new) do |(_, record), t|
         | 
| 165 | 
            +
                  record.tags.each do |tag|
         | 
| 166 | 
            +
                    t.add(tag, source: record.source)
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 163 169 | 
             
              end
         | 
| 164 170 |  | 
| 165 171 | 
             
              private
         | 
    
        data/lib/ollama/image.rb
    CHANGED
    
    | @@ -7,6 +7,8 @@ class Ollama::Image | |
| 7 7 |  | 
| 8 8 | 
             
              attr_accessor :path
         | 
| 9 9 |  | 
| 10 | 
            +
              attr_reader :data
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
              class << self
         | 
| 11 13 | 
             
                def for_base64(data, path: nil)
         | 
| 12 14 | 
             
                  obj = new(data)
         | 
| @@ -31,7 +33,7 @@ class Ollama::Image | |
| 31 33 | 
             
              end
         | 
| 32 34 |  | 
| 33 35 | 
             
              def ==(other)
         | 
| 34 | 
            -
                @data == other | 
| 36 | 
            +
                @data == other.data
         | 
| 35 37 | 
             
              end
         | 
| 36 38 |  | 
| 37 39 | 
             
              def to_s
         | 
| @@ -13,7 +13,7 @@ class Ollama::Utils::CacheFetcher | |
| 13 13 | 
             
                if body && content_type
         | 
| 14 14 | 
             
                  io = StringIO.new(body)
         | 
| 15 15 | 
             
                  io.rewind
         | 
| 16 | 
            -
                  io.extend(Ollama::Utils::Fetcher:: | 
| 16 | 
            +
                  io.extend(Ollama::Utils::Fetcher::HeaderExtension)
         | 
| 17 17 | 
             
                  io.content_type = content_type
         | 
| 18 18 | 
             
                  block.(io)
         | 
| 19 19 | 
             
                end
         | 
| @@ -25,8 +25,8 @@ class Ollama::Utils::CacheFetcher | |
| 25 25 | 
             
                body.empty? and return
         | 
| 26 26 | 
             
                content_type = io.content_type
         | 
| 27 27 | 
             
                content_type.nil? and return
         | 
| 28 | 
            -
                @cache | 
| 29 | 
            -
                @cache | 
| 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 30 | 
             
                self
         | 
| 31 31 | 
             
              end
         | 
| 32 32 |  | 
    
        data/lib/ollama/utils/chooser.rb
    CHANGED
    
    | @@ -8,8 +8,9 @@ module Ollama::Utils::Chooser | |
| 8 8 |  | 
| 9 9 | 
             
              module_function
         | 
| 10 10 |  | 
| 11 | 
            -
              def choose(entries)
         | 
| 11 | 
            +
              def choose(entries, prompt: 'Search? %s')
         | 
| 12 12 | 
             
                entry = Search.new(
         | 
| 13 | 
            +
                  prompt:,
         | 
| 13 14 | 
             
                  match: -> answer {
         | 
| 14 15 | 
             
                    matcher = Amatch::PairDistance.new(answer.downcase)
         | 
| 15 16 | 
             
                    matches = entries.map { |n| [ n, -matcher.similar(n.to_s.downcase) ] }.
         | 
| @@ -19,7 +20,7 @@ module Ollama::Utils::Chooser | |
| 19 20 | 
             
                  },
         | 
| 20 21 | 
             
                  query: -> _answer, matches, selector {
         | 
| 21 22 | 
             
                    matches.each_with_index.map { |m, i|
         | 
| 22 | 
            -
                      i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : "  #{m}"
         | 
| 23 | 
            +
                      i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : "  #{m.to_s}"
         | 
| 23 24 | 
             
                    } * ?\n
         | 
| 24 25 | 
             
                  },
         | 
| 25 26 | 
             
                  found: -> _answer, matches, selector {
         | 
| @@ -27,6 +28,11 @@ module Ollama::Utils::Chooser | |
| 27 28 | 
             
                  },
         | 
| 28 29 | 
             
                  output: STDOUT
         | 
| 29 30 | 
             
                ).start
         | 
| 30 | 
            -
                 | 
| 31 | 
            +
                if entry
         | 
| 32 | 
            +
                  entry
         | 
| 33 | 
            +
                else
         | 
| 34 | 
            +
                  print clear_screen, move_home
         | 
| 35 | 
            +
                  nil
         | 
| 36 | 
            +
                end
         | 
| 31 37 | 
             
              end
         | 
| 32 38 | 
             
            end
         | 
    
        data/lib/ollama/utils/fetcher.rb
    CHANGED
    
    | @@ -6,9 +6,11 @@ require 'stringio' | |
| 6 6 | 
             
            require 'ollama/utils/cache_fetcher'
         | 
| 7 7 |  | 
| 8 8 | 
             
            class Ollama::Utils::Fetcher
         | 
| 9 | 
            -
              module  | 
| 9 | 
            +
              module HeaderExtension
         | 
| 10 10 | 
             
                attr_accessor :content_type
         | 
| 11 11 |  | 
| 12 | 
            +
                attr_accessor :ex
         | 
| 13 | 
            +
             | 
| 12 14 | 
             
                def self.failed
         | 
| 13 15 | 
             
                  object = StringIO.new.extend(self)
         | 
| 14 16 | 
             
                  object.content_type = MIME::Types['text/plain'].first
         | 
| @@ -22,7 +24,7 @@ class Ollama::Utils::Fetcher | |
| 22 24 | 
             
                cache = options.delete(:cache) and
         | 
| 23 25 | 
             
                  cache = Ollama::Utils::CacheFetcher.new(cache)
         | 
| 24 26 | 
             
                if result = cache&.get(url, &block)
         | 
| 25 | 
            -
                  infobar.puts "Getting #{url.inspect} from cache."
         | 
| 27 | 
            +
                  infobar.puts "Getting #{url.to_s.inspect} from cache."
         | 
| 26 28 | 
             
                  return result
         | 
| 27 29 | 
             
                else
         | 
| 28 30 | 
             
                  new(**options).send(:get, url) do |tmp|
         | 
| @@ -39,7 +41,7 @@ class Ollama::Utils::Fetcher | |
| 39 41 | 
             
              def self.read(filename, &block)
         | 
| 40 42 | 
             
                if File.exist?(filename)
         | 
| 41 43 | 
             
                  File.open(filename) do |file|
         | 
| 42 | 
            -
                    file.extend(Ollama::Utils::Fetcher:: | 
| 44 | 
            +
                    file.extend(Ollama::Utils::Fetcher::HeaderExtension)
         | 
| 43 45 | 
             
                    file.content_type = MIME::Types.type_for(filename).first
         | 
| 44 46 | 
             
                    block.(file)
         | 
| 45 47 | 
             
                  end
         | 
| @@ -50,10 +52,10 @@ class Ollama::Utils::Fetcher | |
| 50 52 | 
             
                Tempfile.open do |tmp|
         | 
| 51 53 | 
             
                  IO.popen(command) do |command|
         | 
| 52 54 | 
             
                    until command.eof?
         | 
| 53 | 
            -
                      tmp.write command.read( | 
| 55 | 
            +
                      tmp.write command.read(1 << 14)
         | 
| 54 56 | 
             
                    end
         | 
| 55 57 | 
             
                    tmp.rewind
         | 
| 56 | 
            -
                    tmp.extend(Ollama::Utils::Fetcher:: | 
| 58 | 
            +
                    tmp.extend(Ollama::Utils::Fetcher::HeaderExtension)
         | 
| 57 59 | 
             
                    tmp.content_type = MIME::Types['text/plain'].first
         | 
| 58 60 | 
             
                    block.(tmp)
         | 
| 59 61 | 
             
                  end
         | 
| @@ -63,7 +65,7 @@ class Ollama::Utils::Fetcher | |
| 63 65 | 
             
                if @debug && !e.is_a?(RuntimeError)
         | 
| 64 66 | 
             
                  STDERR.puts "#{e.backtrace * ?\n}"
         | 
| 65 67 | 
             
                end
         | 
| 66 | 
            -
                yield  | 
| 68 | 
            +
                yield HeaderExtension.failed
         | 
| 67 69 | 
             
              end
         | 
| 68 70 |  | 
| 69 71 | 
             
              def initialize(debug: false, http_options: {})
         | 
| @@ -110,7 +112,7 @@ class Ollama::Utils::Fetcher | |
| 110 112 | 
             
                if @debug && !e.is_a?(RuntimeError)
         | 
| 111 113 | 
             
                  STDERR.puts "#{e.backtrace * ?\n}"
         | 
| 112 114 | 
             
                end
         | 
| 113 | 
            -
                yield  | 
| 115 | 
            +
                yield HeaderExtension.failed
         | 
| 114 116 | 
             
              end
         | 
| 115 117 |  | 
| 116 118 | 
             
              def headers
         | 
| @@ -123,12 +125,20 @@ class Ollama::Utils::Fetcher | |
| 123 125 | 
             
                (Excon.defaults[:middlewares] + [ Excon::Middleware::RedirectFollower ]).uniq
         | 
| 124 126 | 
             
              end
         | 
| 125 127 |  | 
| 128 | 
            +
              private
         | 
| 129 | 
            +
             | 
| 126 130 | 
             
              def decorate_io(tmp, response)
         | 
| 127 131 | 
             
                tmp.rewind
         | 
| 128 | 
            -
                tmp.extend( | 
| 132 | 
            +
                tmp.extend(HeaderExtension)
         | 
| 129 133 | 
             
                if content_type = MIME::Types[response.headers['content-type']].first
         | 
| 130 134 | 
             
                  tmp.content_type = content_type
         | 
| 131 135 | 
             
                end
         | 
| 136 | 
            +
                if cache_control = response.headers['cache-control'] and
         | 
| 137 | 
            +
                    cache_control !~ /no-store|no-cache/ and
         | 
| 138 | 
            +
                    ex = cache_control[/s-maxage\s*=\s*(\d+)/, 1] || cache_control[/max-age\s*=\s*(\d+)/, 1]
         | 
| 139 | 
            +
                then
         | 
| 140 | 
            +
                  tmp.ex = ex.to_i
         | 
| 141 | 
            +
                end
         | 
| 132 142 | 
             
              end
         | 
| 133 143 |  | 
| 134 144 | 
             
              def callback(tmp)
         | 
    
        data/lib/ollama/utils/tags.rb
    CHANGED
    
    | @@ -1,12 +1,66 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            class Ollama::Utils::Tags
         | 
| 2 | 
            +
              class Tag < String
         | 
| 3 | 
            +
                include Term::ANSIColor
         | 
| 2 4 |  | 
| 5 | 
            +
                def initialize(tag, source: nil)
         | 
| 6 | 
            +
                  super(tag.to_s)
         | 
| 7 | 
            +
                  self.source = source
         | 
| 8 | 
            +
                end
         | 
| 3 9 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
                 | 
| 10 | 
            +
                attr_accessor :source
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                alias_method :internal, :to_s
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def to_s(link: true)
         | 
| 15 | 
            +
                  tag_string = start_with?(?#) ? super() : ?# + super()
         | 
| 16 | 
            +
                  my_source  = source
         | 
| 17 | 
            +
                  if link && my_source
         | 
| 18 | 
            +
                    unless my_source =~ %r(\A(https?|file)://)
         | 
| 19 | 
            +
                      my_source = 'file://%s' % File.expand_path(my_source)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    hyperlink(my_source) { tag_string }
         | 
| 22 | 
            +
                  else
         | 
| 23 | 
            +
                    tag_string
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def initialize(tags = [], source: nil)
         | 
| 29 | 
            +
                @set = []
         | 
| 30 | 
            +
                tags.each { |tag| add(tag, source:) }
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              def add(tag, source: nil)
         | 
| 34 | 
            +
                unless tag.is_a?(Tag)
         | 
| 35 | 
            +
                  tag = Tag.new(tag, source:)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
                index = @set.bsearch_index { _1 >= tag }
         | 
| 38 | 
            +
                if index == nil
         | 
| 39 | 
            +
                  @set.push(tag)
         | 
| 40 | 
            +
                elsif @set.at(index) != tag
         | 
| 41 | 
            +
                  @set.insert(index, tag)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
                self
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def empty?
         | 
| 47 | 
            +
                @set.empty?
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def size
         | 
| 51 | 
            +
                @set.size
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              def clear
         | 
| 55 | 
            +
                @set.clear
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def each(&block)
         | 
| 59 | 
            +
                @set.each(&block)
         | 
| 7 60 | 
             
              end
         | 
| 61 | 
            +
              include Enumerable
         | 
| 8 62 |  | 
| 9 | 
            -
              def to_s
         | 
| 10 | 
            -
                map { | | 
| 63 | 
            +
              def to_s(link: true)
         | 
| 64 | 
            +
                @set.map { |tag| tag.to_s(link:) } * ' '
         | 
| 11 65 | 
             
              end
         | 
| 12 66 | 
             
            end
         | 
    
        data/lib/ollama/utils/width.rb
    CHANGED
    
    | @@ -2,6 +2,7 @@ require 'tins/terminal' | |
| 2 2 |  | 
| 3 3 | 
             
            module Ollama::Utils::Width
         | 
| 4 4 | 
             
              include Term::ANSIColor
         | 
| 5 | 
            +
              extend Term::ANSIColor
         | 
| 5 6 |  | 
| 6 7 | 
             
              module_function
         | 
| 7 8 |  | 
| @@ -26,10 +27,11 @@ module Ollama::Utils::Width | |
| 26 27 | 
             
                percentage.nil? ^ length.nil? or
         | 
| 27 28 | 
             
                  raise ArgumentError, "either pass percentage or length argument"
         | 
| 28 29 | 
             
                percentage and length ||= width(percentage:)
         | 
| 29 | 
            -
                 | 
| 30 | 
            +
                ellipsis_length = ellipsis.size
         | 
| 31 | 
            +
                if length < ellipsis_length
         | 
| 30 32 | 
             
                  +''
         | 
| 31 | 
            -
                elsif text.size  | 
| 32 | 
            -
                  text[0, length -  | 
| 33 | 
            +
                elsif text.size >= length + ellipsis_length
         | 
| 34 | 
            +
                  text[0, length - ellipsis_length] + ellipsis
         | 
| 33 35 | 
             
                else
         | 
| 34 36 | 
             
                  text
         | 
| 35 37 | 
             
                end
         | 
    
        data/lib/ollama/version.rb
    CHANGED
    
    
    
        data/lib/ollama.rb
    CHANGED
    
    
    
        data/ollama-ruby.gemspec
    CHANGED
    
    | @@ -1,26 +1,26 @@ | |
| 1 1 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            -
            # stub: ollama-ruby 0. | 
| 2 | 
            +
            # stub: ollama-ruby 0.7.0 ruby lib
         | 
| 3 3 |  | 
| 4 4 | 
             
            Gem::Specification.new do |s|
         | 
| 5 5 | 
             
              s.name = "ollama-ruby".freeze
         | 
| 6 | 
            -
              s.version = "0. | 
| 6 | 
            +
              s.version = "0.7.0".freeze
         | 
| 7 7 |  | 
| 8 8 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
         | 
| 9 9 | 
             
              s.require_paths = ["lib".freeze]
         | 
| 10 10 | 
             
              s.authors = ["Florian Frank".freeze]
         | 
| 11 | 
            -
              s.date = "2024- | 
| 11 | 
            +
              s.date = "2024-10-02"
         | 
| 12 12 | 
             
              s.description = "Library that allows interacting with the Ollama API".freeze
         | 
| 13 13 | 
             
              s.email = "flori@ping.de".freeze
         | 
| 14 14 | 
             
              s.executables = ["ollama_console".freeze, "ollama_chat".freeze, "ollama_update".freeze, "ollama_cli".freeze]
         | 
| 15 15 | 
             
              s.extra_rdoc_files = ["README.md".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze]
         | 
| 16 | 
            -
              s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
         | 
| 16 | 
            +
              s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
         | 
| 17 17 | 
             
              s.homepage = "https://github.com/flori/ollama-ruby".freeze
         | 
| 18 18 | 
             
              s.licenses = ["MIT".freeze]
         | 
| 19 19 | 
             
              s.rdoc_options = ["--title".freeze, "Ollama-ruby - Interacting with the Ollama API".freeze, "--main".freeze, "README.md".freeze]
         | 
| 20 20 | 
             
              s.required_ruby_version = Gem::Requirement.new("~> 3.1".freeze)
         | 
| 21 21 | 
             
              s.rubygems_version = "3.5.18".freeze
         | 
| 22 22 | 
             
              s.summary = "Interacting with the Ollama API".freeze
         | 
| 23 | 
            -
              s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze]
         | 
| 23 | 
            +
              s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze]
         | 
| 24 24 |  | 
| 25 25 | 
             
              s.specification_version = 4
         | 
| 26 26 |  | 
| @@ -38,7 +38,6 @@ Gem::Specification.new do |s| | |
| 38 38 | 
             
              s.add_runtime_dependency(%q<redis>.freeze, ["~> 5.0".freeze])
         | 
| 39 39 | 
             
              s.add_runtime_dependency(%q<numo-narray>.freeze, ["~> 0.9".freeze])
         | 
| 40 40 | 
             
              s.add_runtime_dependency(%q<more_math>.freeze, ["~> 1.1".freeze])
         | 
| 41 | 
            -
              s.add_runtime_dependency(%q<sorted_set>.freeze, ["~> 1.0".freeze])
         | 
| 42 41 | 
             
              s.add_runtime_dependency(%q<mime-types>.freeze, ["~> 3.0".freeze])
         | 
| 43 42 | 
             
              s.add_runtime_dependency(%q<reverse_markdown>.freeze, ["~> 2.0".freeze])
         | 
| 44 43 | 
             
              s.add_runtime_dependency(%q<complex_config>.freeze, ["~> 0.22".freeze])
         | 
| @@ -48,4 +47,5 @@ Gem::Specification.new do |s| | |
| 48 47 | 
             
              s.add_runtime_dependency(%q<logger>.freeze, ["~> 1.0".freeze])
         | 
| 49 48 | 
             
              s.add_runtime_dependency(%q<json>.freeze, ["~> 2.0".freeze])
         | 
| 50 49 | 
             
              s.add_runtime_dependency(%q<xdg>.freeze, ["~> 7.0".freeze])
         | 
| 50 | 
            +
              s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.34".freeze])
         | 
| 51 51 | 
             
            end
         | 
    
        data/spec/ollama/image_spec.rb
    CHANGED
    
    | @@ -9,6 +9,11 @@ RSpec.describe Ollama::Image do | |
| 9 9 | 
             
                expect(image).to be_a described_class
         | 
| 10 10 | 
             
              end
         | 
| 11 11 |  | 
| 12 | 
            +
              it 'can be equal or not' do
         | 
| 13 | 
            +
                expect(image).not_to eq described_class.for_string('')
         | 
| 14 | 
            +
                expect(image).to eq described_class.for_filename(asset('kitten.jpg'))
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 12 17 | 
             
              it 'cannot be created via .new' do
         | 
| 13 18 | 
             
                expect {
         | 
| 14 19 | 
             
                  described_class.new('nix')
         | 
| @@ -33,10 +33,11 @@ RSpec.describe Ollama::Utils::CacheFetcher do | |
| 33 33 |  | 
| 34 34 | 
             
              it 'has #put' do
         | 
| 35 35 | 
             
                io = StringIO.new('world')
         | 
| 36 | 
            -
                io.extend(Ollama::Utils::Fetcher:: | 
| 36 | 
            +
                io.extend(Ollama::Utils::Fetcher::HeaderExtension)
         | 
| 37 37 | 
             
                io.content_type = MIME::Types['text/plain'].first
         | 
| 38 | 
            -
                 | 
| 39 | 
            -
                expect(cache).to receive(: | 
| 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)
         | 
| 40 41 | 
             
                fetcher.put(url, io)
         | 
| 41 42 | 
             
              end
         | 
| 42 43 | 
             
            end
         | 
| @@ -14,11 +14,35 @@ RSpec.describe Ollama::Utils::Tags do | |
| 14 14 | 
             
                tags = described_class.new([ 'foo' ])
         | 
| 15 15 | 
             
                tags.add 'bar'
         | 
| 16 16 | 
             
                expect(tags.to_a).to eq %w[ bar foo ]
         | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              it 'can increase in size' do
         | 
| 20 | 
            +
                tags = described_class.new
         | 
| 21 | 
            +
                expect { tags.add 'foo' }.to change { tags.size }.from(0).to(1)
         | 
| 22 | 
            +
                expect { tags.add 'bar' }.to change { tags.size }.from(1).to(2)
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              it 'can be cleared' do
         | 
| 26 | 
            +
                tags = described_class.new([ 'foo', 'bar' ])
         | 
| 27 | 
            +
                expect { tags.clear }.to change { tags.size }.from(2).to(0)
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              it 'tags can be empt' do
         | 
| 31 | 
            +
                tags = described_class.new([ 'foo' ])
         | 
| 32 | 
            +
                expect { tags.clear }.to change { tags.empty? }.from(false).to(true)
         | 
| 19 33 | 
             
              end
         | 
| 20 34 |  | 
| 21 35 | 
             
              it 'can be output nicely' do
         | 
| 22 36 | 
             
                expect(described_class.new(%w[ foo bar ]).to_s).to eq '#bar #foo'
         | 
| 23 37 | 
             
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              it 'can be output nicely with links to source' do
         | 
| 40 | 
            +
                tags = described_class.new([ 'foo' ], source: 'https://foo.example.com')
         | 
| 41 | 
            +
                tags.add 'bar', source: '/path/to/bar.html'
         | 
| 42 | 
            +
                expect(tags.to_a).to eq %w[ bar foo ]
         | 
| 43 | 
            +
                tags.all? { expect(_1).to be_a(Ollama::Utils::Tags::Tag) }
         | 
| 44 | 
            +
                expect(tags.to_s).to eq(
         | 
| 45 | 
            +
                  "\e]8;;file:///path/to/bar.html\e\\#bar\e]8;;\e\\ \e]8;;https://foo.example.com\e\\#foo\e]8;;\e\\"
         | 
| 46 | 
            +
                )
         | 
| 47 | 
            +
              end
         | 
| 24 48 | 
             
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            RSpec.describe Ollama::Utils::Width do
         | 
| 4 | 
            +
              before do
         | 
| 5 | 
            +
                allow(Tins::Terminal).to receive(:columns).and_return 80
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe '.width' do
         | 
| 9 | 
            +
                it 'defaults to 100%' do
         | 
| 10 | 
            +
                  expect(described_class.width).to eq 80
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                it 'can be to 80%' do
         | 
| 14 | 
            +
                  expect(described_class.width(percentage: 80)).to eq 64
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              describe '.wrap' do
         | 
| 19 | 
            +
                it 'can wrap with percentage' do
         | 
| 20 | 
            +
                  wrapped = described_class.wrap([ ?A * 10 ] * 10 * ' ', percentage: 80)
         | 
| 21 | 
            +
                  expect(wrapped).to eq(
         | 
| 22 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA\n"\
         | 
| 23 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA"
         | 
| 24 | 
            +
                  )
         | 
| 25 | 
            +
                  expect(wrapped.size).to eq 109
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                it 'can wrap with length' do
         | 
| 29 | 
            +
                  wrapped = described_class.wrap([ ?A * 10 ] * 10 * ' ', length: 64)
         | 
| 30 | 
            +
                  expect(wrapped).to eq(
         | 
| 31 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA\n"\
         | 
| 32 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA"
         | 
| 33 | 
            +
                  )
         | 
| 34 | 
            +
                  expect(wrapped.size).to eq 109
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                it "doesn't wrap with length 0" do
         | 
| 38 | 
            +
                  wrapped = described_class.wrap([ ?A * 10 ] * 10 * ' ', length: 0)
         | 
| 39 | 
            +
                  expect(wrapped).to eq(
         | 
| 40 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA "\
         | 
| 41 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA"
         | 
| 42 | 
            +
                  )
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              describe '.truncate' do
         | 
| 47 | 
            +
                it 'can truncate with percentage' do
         | 
| 48 | 
            +
                  truncated = described_class.truncate([ ?A * 10 ] * 10 * ' ', percentage: 80)
         | 
| 49 | 
            +
                  expect(truncated).to eq(
         | 
| 50 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAA…"
         | 
| 51 | 
            +
                  )
         | 
| 52 | 
            +
                  expect(truncated.size).to eq 64
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                it 'can truncate with length' do
         | 
| 56 | 
            +
                  truncated = described_class.truncate([ ?A * 10 ] * 10 * ' ', length: 64)
         | 
| 57 | 
            +
                  expect(truncated).to eq(
         | 
| 58 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAA…"
         | 
| 59 | 
            +
                  )
         | 
| 60 | 
            +
                  expect(truncated.size).to eq 64
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                it 'cannot truncate if not necessary' do
         | 
| 64 | 
            +
                  text = [ ?A * 10 ] * 5 * ' '
         | 
| 65 | 
            +
                  truncated = described_class.truncate(text, length: 54)
         | 
| 66 | 
            +
                  expect(truncated).to eq text
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                it 'can truncate with length 0' do
         | 
| 70 | 
            +
                  truncated = described_class.truncate([ ?A * 10 ] * 10 * ' ', length: 0)
         | 
| 71 | 
            +
                  expect(truncated).to be_empty
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                it 'can truncate with ...' do
         | 
| 75 | 
            +
                  truncated = described_class.truncate([ ?A * 10 ] * 10 * ' ', length: 64, ellipsis: '...')
         | 
| 76 | 
            +
                  expect(truncated).to eq(
         | 
| 77 | 
            +
                    "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAA..."
         | 
| 78 | 
            +
                  )
         | 
| 79 | 
            +
                  expect(truncated.size).to eq 64
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ollama-ruby
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Florian Frank
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-10-02 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: gem_hadar
         | 
| @@ -206,20 +206,6 @@ dependencies: | |
| 206 206 | 
             
                - - "~>"
         | 
| 207 207 | 
             
                  - !ruby/object:Gem::Version
         | 
| 208 208 | 
             
                    version: '1.1'
         | 
| 209 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 210 | 
            -
              name: sorted_set
         | 
| 211 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 212 | 
            -
                requirements:
         | 
| 213 | 
            -
                - - "~>"
         | 
| 214 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 215 | 
            -
                    version: '1.0'
         | 
| 216 | 
            -
              type: :runtime
         | 
| 217 | 
            -
              prerelease: false
         | 
| 218 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 219 | 
            -
                requirements:
         | 
| 220 | 
            -
                - - "~>"
         | 
| 221 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 222 | 
            -
                    version: '1.0'
         | 
| 223 209 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 224 210 | 
             
              name: mime-types
         | 
| 225 211 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -346,6 +332,20 @@ dependencies: | |
| 346 332 | 
             
                - - "~>"
         | 
| 347 333 | 
             
                  - !ruby/object:Gem::Version
         | 
| 348 334 | 
             
                    version: '7.0'
         | 
| 335 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 336 | 
            +
              name: tins
         | 
| 337 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 338 | 
            +
                requirements:
         | 
| 339 | 
            +
                - - "~>"
         | 
| 340 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 341 | 
            +
                    version: '1.34'
         | 
| 342 | 
            +
              type: :runtime
         | 
| 343 | 
            +
              prerelease: false
         | 
| 344 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 345 | 
            +
                requirements:
         | 
| 346 | 
            +
                - - "~>"
         | 
| 347 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 348 | 
            +
                    version: '1.34'
         | 
| 349 349 | 
             
            description: Library that allows interacting with the Ollama API
         | 
| 350 350 | 
             
            email: flori@ping.de
         | 
| 351 351 | 
             
            executables:
         | 
| @@ -519,6 +519,7 @@ files: | |
| 519 519 | 
             
            - spec/ollama/utils/fetcher_spec.rb
         | 
| 520 520 | 
             
            - spec/ollama/utils/file_argument_spec.rb
         | 
| 521 521 | 
             
            - spec/ollama/utils/tags_spec.rb
         | 
| 522 | 
            +
            - spec/ollama/utils/width_spec.rb
         | 
| 522 523 | 
             
            - spec/spec_helper.rb
         | 
| 523 524 | 
             
            - tmp/.keep
         | 
| 524 525 | 
             
            homepage: https://github.com/flori/ollama-ruby
         | 
| @@ -587,4 +588,5 @@ test_files: | |
| 587 588 | 
             
            - spec/ollama/utils/fetcher_spec.rb
         | 
| 588 589 | 
             
            - spec/ollama/utils/file_argument_spec.rb
         | 
| 589 590 | 
             
            - spec/ollama/utils/tags_spec.rb
         | 
| 591 | 
            +
            - spec/ollama/utils/width_spec.rb
         | 
| 590 592 | 
             
            - spec/spec_helper.rb
         |