ollama-ruby 1.21.0 → 1.22.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ba6bf03f34b785b65a26f7bcdeb2d77d00c8f4d2403987d31258b1f78d27680
4
- data.tar.gz: ee8337571ff29a5fb2db7f514ec4037ce4dca00da9193d18f80cff671dde06a3
3
+ metadata.gz: e4e3eda370660a57c7382a3548702359bdb3ea37afc2c785a26fc0ef1f199cd7
4
+ data.tar.gz: 77f392bc3c35948514cd78543560f3057b7b921487abd5803cad6f6f1cea9ac7
5
5
  SHA512:
6
- metadata.gz: 0f70a40fe9cc263e073df7f30e358d3ed9ff416b74ebf281cd9324ae73bcf2c27d23c3e41e92a5d1524b013a39706f243322b9344a9ee68759ea5ea214286dce
7
- data.tar.gz: a6091750f7b86920191ff2dca0efde302ecfbd2f2af11a43bb41b8b01ac44016b9b273a15461b6d2f582dbff05be9162139cf9305d4fa85ce67e4010344f2637
6
+ metadata.gz: c7633fbcb2ed35181d8ffbdf129f4e151f1328ade92df8279a7f62ced065e3abfd05f0c5c93ac06871dc7421b5567d9c2b963a41a97ddd4e8fe51e8bd7ca7aeb
7
+ data.tar.gz: 93bdea4a001a9fb416100b6e5230c8a11d5524fe036b6216ccecc58e49584a703a1249e24510f1d66871866bcdd6483b703a4f47d0386253b0c5c3db613837d7
data/CHANGES.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-06-21 v1.22.0
4
+
5
+ ### New Features
6
+
7
+ - Added support for blob uploads and management:
8
+ - Implemented `Ollama::Client#upload_file` using `Excon` `:request_block`
9
+ for memory-efficient streaming.
10
+ - Introduced `Ollama::Commands::PushBlob` for binary data uploads with
11
+ automatic SHA256 calculation.
12
+ - Added `Ollama::Client#blob_exists?` and `Ollama::Commands::BlobExists` to
13
+ verify blobs via the API.
14
+ - Introduced `Ollama::Digester` for SHA256 binary hashing, featuring
15
+ `compute_digest` for IO streams and a `prefix_sha256` utility for conditional
16
+ digest formatting.
17
+ - Added visual progress tracking to digest computation using `infobar`, IEC
18
+ units, and braille spinner frames.
19
+
20
+ ### Documentation
21
+
22
+ - Added a "Importing Custom Models" section to the `README.md`, documenting the
23
+ workflow using `push_blob` and `create` with Ruby **3** shorthand syntax
24
+ examples.
25
+ - Enhanced YARD documentation for `prefix_sha256` and internal command options.
26
+
27
+ ### Improvements & Refactors
28
+
29
+ - Mapped network exceptions (`Excon::Errors::SocketError`,
30
+ `Excon::Errors::Timeout`, and `Excon::Error`) to internal `Ollama::Errors`.
31
+ - Updated `Ollama::Handlers::Concern` to change `result` from an `attr_reader`
32
+ to an `attr_accessor`.
33
+ - Added a `skip_doc` option to `Ollama::Client::Command.command` to suppress
34
+ automatic documentation for internal commands.
35
+ - Fixed formatting in `show_spec.rb`.
36
+
37
+ ### Dependencies
38
+
39
+ - Updated `rubygems_version` in the gemspec to **4.0.10**.
40
+ - Bumped minimum `gem_hadar` development dependency to **2.17.1**.
41
+
3
42
  ## 2026-03-20 v1.21.0
4
43
 
5
44
  - Added a template accessor to Ollama::Commands::Create, exposing a new
data/README.md CHANGED
@@ -69,6 +69,37 @@ messages = Message.new(role: 'user', content: 'Why is the sky blue?')
69
69
  ollama.chat(model: 'llama3.1', stream: true, messages:, &Print)
70
70
  ```
71
71
 
72
+ ### Importing Custom Models
73
+
74
+ You can import your own model files (e.g., `.gguf`) directly into Ollama. This
75
+ is a two-step process: first, upload the binary file to obtain a SHA256 digest,
76
+ and then create the model using that digest as a reference.
77
+
78
+ This workflow is particularly convenient in the `ollama_console`:
79
+
80
+ ```ruby
81
+ # 1. Upload the binary blob to get its digest
82
+ # This will show a progress bar for both hashing and uploading
83
+ digest = push_blob(body: File.new('my-model-q4_0.gguf', 'rb'))
84
+
85
+ # 2. Define the model configuration
86
+ template = "<|system|>\n{{ .System }}</s>\n<|user|>\n{{ .Prompt }}</s>\n<|assistant|>\n"
87
+ system = "You are a helpful AI assistant."
88
+ parameters = { stop: ["<|system|>", "<|user|>", "<|assistant|>", "</s>"] }
89
+
90
+ # 3. Create the model using the digest (using Ruby 3 shorthand)
91
+ create(
92
+ model: 'my-username/my-model:latest',
93
+ files: { 'my-model-q4_0.gguf' => digest },
94
+ template:,
95
+ system:,
96
+ parameters:
97
+ )
98
+ ```
99
+
100
+ This method is highly efficient as it uses streaming uploads and avoids
101
+ redundant data transfers if the blob already exists on the server.
102
+
72
103
  ## Try out things in ollama\_console
73
104
 
74
105
  This is an interactive console where you can try out the different commands
@@ -23,16 +23,20 @@ module Ollama::Client::Command
23
23
  # the command supports streaming and the presence of an explicit handler.
24
24
  #
25
25
  # @param name [ Symbol ] the name of the command to define
26
- # @param default_handler [ Class ] the default handler class to use when no explicit handler is provided
27
- # @param stream_handler [ Class, nil ] the handler class to use for streaming responses, if applicable
26
+ # @param default_handler [ Class ] the default handler class to use when no
27
+ # explicit handler is provided
28
+ # @param stream_handler [ Class, nil ] the handler class to use for
29
+ # streaming responses, if applicable
30
+ # @param skip_doc [ Boolean ] whether to omit this command from the
31
+ # client's documented commands list, defaults to false
28
32
  #
29
33
  # @note Create Command `name`, if `stream` was true, set `stream_handler`
30
34
  # as default, otherwise `default_handler`.
31
35
  #
32
36
  # @return [ self ] returns the receiver after defining the command method
33
- def command(name, default_handler:, stream_handler: nil)
37
+ def command(name, default_handler:, stream_handler: nil, skip_doc: false)
34
38
  klass = Ollama::Commands.const_get(name.to_s.camelize)
35
- doc Ollama::Client::Doc.new(name)
39
+ skip_doc or doc Ollama::Client::Doc.new(name)
36
40
  define_method(name) do |**parameters, &handler|
37
41
  instance = klass.new(**parameters)
38
42
  instance.client = self
data/lib/ollama/client.rb CHANGED
@@ -123,6 +123,10 @@ class Ollama::Client
123
123
 
124
124
  command(:version, default_handler: Single)
125
125
 
126
+ command(:blob_exists, default_handler: NOP, skip_doc: true)
127
+
128
+ command(:push_blob, default_handler: Progress, skip_doc: true)
129
+
126
130
  # The commands method retrieves and sorts the documented commands available
127
131
  # in the client.
128
132
  #
@@ -199,6 +203,78 @@ class Ollama::Client
199
203
  raise Ollama::Errors::Error, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
200
204
  end
201
205
 
206
+ # The upload_file method handles the streaming upload of a file to the server.
207
+ #
208
+ # This method opens the specified file in binary mode and utilizes Excon's
209
+ # :request_block to stream the content in chunks. It simultaneously updates
210
+ # the provided progress handler with current upload stats.
211
+ #
212
+ # @param command [ Ollama::Commands::PushBlob ] the command instance containing path logic
213
+ # @param body [ IO ] the body to be uploaded
214
+ # @param handler [ Ollama::Handler ] the handler object responsible for processing API responses
215
+ #
216
+ # @return [ Ollama::Client ] returns the client instance itself after initiating the request
217
+ def upload_file(path:, body:, handler:)
218
+ body.binmode
219
+ body.rewind
220
+ total_size = body.size
221
+ bytes_sent = 0
222
+ new_headers = {
223
+ 'Content-Type' => 'application/octet-stream',
224
+ 'Content-Length' => total_size,
225
+ }
226
+ request_block = -> do
227
+ chunk = body.read(Excon.defaults[:chunk_size]).to_s
228
+ bytes_sent += chunk.bytesize
229
+ fake_response = Ollama::Response.new(
230
+ completed: bytes_sent,
231
+ total: total_size,
232
+ status: 'Uploading…'
233
+ )
234
+ handler.call(fake_response)
235
+ chunk
236
+ end
237
+
238
+ url = @base_url + path
239
+ response = excon(url).post(
240
+ headers: headers.merge(new_headers),
241
+ request_block:
242
+ )
243
+
244
+ case response.status
245
+ when 200, 201
246
+ # Success
247
+ else
248
+ raise Ollama::Errors::Error, "#{response.status} #{response.body.inspect}"
249
+ end
250
+ self
251
+ rescue Excon::Errors::SocketError => e
252
+ raise Ollama::Errors::SocketError, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
253
+ rescue Excon::Errors::Timeout => e
254
+ raise Ollama::Errors::TimeoutError, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
255
+ rescue Excon::Error => e
256
+ raise Ollama::Errors::Error, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
257
+ ensure
258
+ body.rewind
259
+ end
260
+
261
+ # The blob_exists? method verifies if a binary blob exists on the Ollama
262
+ # server.
263
+ #
264
+ # This convenience method checks for the existence of a blob identified by
265
+ # its digest and returns a boolean value, simplifying the process of
266
+ # verifying blobs before attempting an upload.
267
+ #
268
+ # @param digest [ String ] the SHA256 digest of the blob (e.g., 'sha256:...')
269
+ # @return [ TrueClass, FalseClass ] true if the blob exists on the server,
270
+ # false otherwise
271
+ def blob_exists?(digest)
272
+ blob_exists(digest:)
273
+ true
274
+ rescue Ollama::Errors::NotFoundError
275
+ false
276
+ end
277
+
202
278
  # The inspect method returns a string representation of the client instance.
203
279
  #
204
280
  # This method provides a human-readable description of the client object,
@@ -0,0 +1,73 @@
1
+ require 'ollama/digester'
2
+
3
+ # A command class that represents the blob existence check API endpoint for
4
+ # Ollama.
5
+ #
6
+ # This class is used to interact with the Ollama API's blobs endpoint using a
7
+ # HEAD request to verify if a specific binary blob (identified by its digest)
8
+ # already exists on the server. This is particularly useful for optimizing
9
+ # model uploads by avoiding redundant transfers of large files.
10
+ #
11
+ # @example Checking if a blob exists
12
+ # exists = ollama.blob_exists?(digest: 'sha256:...')
13
+ class Ollama::Commands::BlobExists
14
+ include Ollama::Digester
15
+
16
+ # The initialize method sets up a new instance with the target digest.
17
+ #
18
+ # This method initializes the command object with the specific digest of the
19
+ # blob being checked and explicitly disables streaming as HEAD requests
20
+ # are inherently non-streaming.
21
+ #
22
+ # @param digest [ String, nil ] the SHA256 digest of the blob (e.g., 'sha256:...')
23
+ # @param blob [ IO, nil ] the binary data stream to be hashed if no digest is
24
+ # provided
25
+ def initialize(digest: nil, blob: nil)
26
+ digest = prefix_sha256(digest)
27
+ if digest.nil? && blob
28
+ digest = compute_digest(blob)
29
+ end
30
+ digest or raise ArgumentError, 'require digest or blob to perform'
31
+ @digest, @stream = digest, false
32
+ end
33
+
34
+ # The digest attribute reader returns the target blob digest.
35
+ #
36
+ # @return [ String ] the SHA256 digest of the blob
37
+ attr_reader :digest
38
+
39
+ # The stream attribute reader returns the streaming behavior setting.
40
+ #
41
+ # @return [ FalseClass ] the streaming behavior flag (always false for this command)
42
+ attr_reader :stream
43
+
44
+ # The client attribute writer allows setting the client instance associated
45
+ # with the object.
46
+ #
47
+ # @attr_writer [ Ollama::Client ] the assigned client instance
48
+ attr_writer :client
49
+
50
+ # The path method constructs the API endpoint URL for a specific binary blob.
51
+ # It interpolates the target blob's digest into the path string to create the
52
+ # correct resource identifier for the Ollama API.
53
+ #
54
+ # @return [ String ] the API endpoint path for the blob
55
+ def path
56
+ "/api/blobs/#{digest}"
57
+ end
58
+
59
+ # The perform method executes the existence check request using a HEAD method.
60
+ #
61
+ # This method initiates a HEAD request to the '/api/blobs/:digest' endpoint.
62
+ # A successful response (typically 200 OK) indicates the blob exists, while
63
+ # a 404 Not Found indicates it does not.
64
+ #
65
+ # @param handler [ Ollama::Handler ] the handler object responsible for
66
+ # processing API responses
67
+ #
68
+ # @return [ self ] returns the current instance after initiating the request
69
+ def perform(handler)
70
+ @client.request(method: :head, path:, stream:, handler:)
71
+ self
72
+ end
73
+ end
@@ -0,0 +1,74 @@
1
+ require 'ollama/digester'
2
+
3
+ # A command class that represents the push blob API endpoint for Ollama.
4
+ #
5
+ # This class is used to upload raw binary files (blobs) to the Ollama server.
6
+ # It accepts the content to be uploaded and automatically calculates the
7
+ # required SHA256 digest to target the correct API endpoint.
8
+ #
9
+ # @example Preparing a push blob command with a file
10
+ # command = Ollama::Commands::PushBlob.new(body: File.open('model.gguf', 'rb'))
11
+ class Ollama::Commands::PushBlob
12
+ include Ollama::Digester
13
+
14
+ # The initialize method sets up a new instance and calculates the content
15
+ # digest.
16
+ #
17
+ # This method ensures the body is an IO-like object and computes the SHA256
18
+ # hash of the content to be used as the target endpoint path.
19
+ #
20
+ # @param body [ IO, String ] the binary content or file handle to upload
21
+ # @param digest [ String, nil ] optional precomputed SHA256 digest. If provided,
22
+ # it takes precedence over computing one from the body.
23
+ def initialize(body:, digest: nil)
24
+ @body = body.respond_to?(:read) ? body : StringIO.new(body.to_str)
25
+ digest = prefix_sha256(digest)
26
+ @digest = digest || compute_digest(@body)
27
+ @stream = false
28
+ end
29
+
30
+ # The body attribute reader returns the binary content to be uploaded to the
31
+ # Ollama server.
32
+ #
33
+ # @attr_reader [ IO, String ] the raw image or model data used for the blob upload
34
+ attr_reader :body
35
+
36
+ # The digest attribute reader returns the target blob digest.
37
+ #
38
+ # @return [ String ] the SHA256 digest of the blob
39
+ attr_reader :digest
40
+
41
+ # The stream attribute reader returns the streaming behavior setting.
42
+ #
43
+ # @return [ FalseClass ] the streaming behavior flag (always false for this command)
44
+ attr_reader :stream
45
+
46
+ # The client attribute writer allows setting the client instance associated
47
+ # with the object.
48
+ #
49
+ # @attr_writer [ Ollama::Client ] the assigned client instance
50
+ attr_writer :client
51
+
52
+ # The path method returns the API endpoint path for pushing blobs.
53
+ #
54
+ # This method interpolates the digest into the URL to target a specific blob.
55
+ #
56
+ # @return [ String ] the API endpoint path '/api/blobs/<digest>'
57
+ def path
58
+ "/api/blobs/#{digest}"
59
+ end
60
+
61
+ # The perform method is not used directly for file uploads in this
62
+ # implementation, as the client uses a specialized streaming request method
63
+ # to handle IO.
64
+ # However, it's kept for interface consistency.
65
+ #
66
+ # @param handler [ Ollama::Handler ] the handler object responsible for processing API responses
67
+ #
68
+ # @return [ self ] returns the current instance after initiating the request
69
+ def perform(handler)
70
+ @client.blob_exists?(digest) or @client.upload_file(path:, body:, handler:)
71
+ handler.result = digest
72
+ self
73
+ end
74
+ end
@@ -0,0 +1,53 @@
1
+ require 'digest'
2
+
3
+ # Provides utility methods for computing SHA256 digests of binary streams.
4
+ # This module is designed as a mixin to provide consistent hashing logic across
5
+ # different components, ensuring that IO objects are handled safely and
6
+ # efficiently.
7
+ module Ollama::Digester
8
+ private
9
+
10
+ # Calculates the SHA256 checksum of a given IO object.
11
+ #
12
+ # The method ensures the stream is reset both before and after processing to
13
+ # maintain the state of the IO object. It reads the content in chunks to
14
+ # minimize memory footprint when dealing with large binary files.
15
+ #
16
+ # @param io [ IO ] the binary data stream to be hashed
17
+ # @param chunk_size [ Integer ] the size of chunks to read from the IO stream (default: 16384)
18
+ #
19
+ # @return [ String ] the resulting SHA256 digest formatted as 'sha256:<hex>'
20
+ def compute_digest(io, chunk_size: 1 << 16)
21
+ io.binmode
22
+ io.rewind
23
+ label = 'Computing Digest…'
24
+ total = io.size
25
+ message = {
26
+ format: '%l %c/%t in %te, ETA %e @%E %s',
27
+ '%c' => { format: '%.2f %U', unit: ?B, prefix: :iec_uc },
28
+ '%t' => { format: '%2.2f %U', unit: ?B, prefix: :iec_uc },
29
+ '%s' => { frames: :braille181 },
30
+ }
31
+ infobar.(total:, label:, message:)
32
+ digest = Digest::SHA256.new
33
+ until io.eof?
34
+ infobar.progress(by: chunk_size)
35
+ digest << io.read(chunk_size)
36
+ end
37
+ infobar.newline
38
+ 'sha256:%s' % digest.hexdigest
39
+ ensure
40
+ io.rewind
41
+ end
42
+
43
+ # Ensures a SHA256 digest is prefixed with 'sha256:'.
44
+ #
45
+ # If the provided string consists of exactly 64 hexadecimal characters,
46
+ # the prefix is prepended. Otherwise, the original string is returned.
47
+ #
48
+ # @param digest [ String, nil ] the digest to be prefixed
49
+ # @return [ String, nil ] the prefixed digest or the original value
50
+ def prefix_sha256(digest)
51
+ digest&.sub(/\A(?=\h{64}\z)/, 'sha256:')
52
+ end
53
+ end
@@ -27,17 +27,11 @@ module Ollama::Handlers::Concern
27
27
  # messages are written
28
28
  attr_reader :output
29
29
 
30
- # The result method returns the collected response data from handler operations.
30
+ # The result attribute allows reading and setting the final outcome of a
31
+ # command operation.
31
32
  #
32
- # This method provides access to the accumulated results after a command has
33
- # been executed with a handler that collects responses, such as Collector or
34
- # Single handlers.
35
- #
36
- # @return [ Ollama::Response, Array<Ollama::Response>, nil ] the result of the handler operation,
37
- # which may be a single response, an array of responses, or nil
38
- # depending on the handler type and the command execution
39
- attr_reader :result
40
-
33
+ # @attr [ Object ] the processed result or return value generated by the handler
34
+ attr_accessor :result
41
35
 
42
36
  # The implement :call, :subclass line enforces that any class including this
43
37
  # concern must implement a `call` instance method. This creates a contract
@@ -76,8 +76,9 @@ class Ollama::Handlers::Progress
76
76
  # @param current [ Integer ] the current progress value
77
77
  # @param total [ Integer ] the total progress value
78
78
  #
79
- # @return [ String ] a formatted progress message containing current status,
80
- # time information, and estimated completion details
79
+ # @return [ Hash ] a formatted progress message containing current status,
80
+ # time information, and estimated completion details, with a braille
81
+ # spinner.
81
82
  def message(current, total)
82
83
  progress = '%s/%s' % [ current, total ].map {
83
84
  Tins::Unit.format(_1, format: '%.2f %U', unit: ?B, prefix: :iec_uc)
@@ -1,6 +1,6 @@
1
1
  module Ollama
2
2
  # Ollama version
3
- VERSION = '1.21.0'
3
+ VERSION = '1.22.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/lib/ollama.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'uri'
2
3
  require 'excon'
3
4
  require 'tins'
4
5
  require 'tins/xt/full'
@@ -66,5 +67,7 @@ require 'ollama/commands/embed'
66
67
  require 'ollama/commands/embeddings'
67
68
  require 'ollama/commands/ps'
68
69
  require 'ollama/commands/version'
70
+ require 'ollama/commands/blob_exists'
71
+ require 'ollama/commands/push_blob'
69
72
 
70
73
  require 'ollama/client'
data/ollama-ruby.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama-ruby 1.21.0 ruby lib
2
+ # stub: ollama-ruby 1.22.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama-ruby".freeze
6
- s.version = "1.21.0".freeze
6
+ s.version = "1.22.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]
@@ -12,19 +12,19 @@ Gem::Specification.new do |s|
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_update".freeze, "ollama_cli".freeze, "ollama_browse".freeze, "ollama_ps".freeze]
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/configuration/config.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/commands/version.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/json_loader.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/version.rb".freeze]
16
- s.files = [".envrc".freeze, ".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_browse".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_ps".freeze, "bin/ollama_update".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/configuration/config.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/commands/version.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/json_loader.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/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/client.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/options.json".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/commands/version_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/spec_helper.rb".freeze, "tmp/.keep".freeze]
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/configuration/config.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/blob_exists.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/push_blob.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/commands/version.rb".freeze, "lib/ollama/digester.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/json_loader.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/version.rb".freeze]
16
+ s.files = [".envrc".freeze, ".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_browse".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_ps".freeze, "bin/ollama_update".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/configuration/config.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/blob_exists.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/push_blob.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/commands/version.rb".freeze, "lib/ollama/digester.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/json_loader.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/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/client.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/options.json".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/blob_exists_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_blob_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/commands/version_spec.rb".freeze, "spec/ollama/digester_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/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
- s.rubygems_version = "4.0.8".freeze
21
+ s.rubygems_version = "4.0.10".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/commands/version_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/spec_helper.rb".freeze]
23
+ s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/blob_exists_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_blob_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/commands/version_spec.rb".freeze, "spec/ollama/digester_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/spec_helper.rb".freeze]
24
24
 
25
25
  s.specification_version = 4
26
26
 
27
- s.add_development_dependency(%q<gem_hadar>.freeze, [">= 2.17.0".freeze])
27
+ s.add_development_dependency(%q<gem_hadar>.freeze, [">= 2.17.1".freeze])
28
28
  s.add_development_dependency(%q<all_images>.freeze, ["~> 0.12".freeze])
29
29
  s.add_development_dependency(%q<rspec>.freeze, ["~> 3.2".freeze])
30
30
  s.add_development_dependency(%q<kramdown>.freeze, ["~> 2.0".freeze])
@@ -193,4 +193,98 @@ describe Ollama::Client do
193
193
  expect($stdout).to receive(:puts).with(/Commands:.*?chat/)
194
194
  ollama.help
195
195
  end
196
+
197
+ context 'blobs' do
198
+ it 'can verify that a blob exists' do
199
+ expect(excon).to receive(:head).
200
+ and_return(double('Response', status: 200, body: ''))
201
+ exists = ollama.blob_exists?(
202
+ 'sha256:5ee4f07cdb9beadbbb293e85803c569b01bd37ed059d2715faa7bb405f31caa6'
203
+ )
204
+ expect(exists).to eq true
205
+ end
206
+
207
+ it 'can falsify that a blob exists' do
208
+ expect(excon).to receive(:head).and_raise Ollama::Errors::NotFoundError
209
+ exists = ollama.blob_exists?(
210
+ 'sha256:5ee4f07cdb9beadbbb293e85803c569b01bd37ed059d2715faa7bb405f31ca6a'
211
+ )
212
+ expect(exists).to eq false
213
+ end
214
+
215
+ it 'can upload a file and return self on success' do
216
+ body = StringIO.new('dummy content')
217
+ handler = double('Handler', call: nil)
218
+
219
+ expect(excon).to receive(:post).with(
220
+ headers: hash_including('Content-Type' => 'application/octet-stream'),
221
+ request_block: an_instance_of(Proc)
222
+ ).and_return(double(status: 201, body: ''))
223
+
224
+ expect(ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)).to eq ollama
225
+ end
226
+
227
+ it 'can report progress via the handler' do
228
+ content = 'Hello Ruby!'
229
+ body = StringIO.new(content)
230
+ handler = double('Handler')
231
+
232
+ # We capture the block to simulate Excon calling it
233
+ captured_block = nil
234
+ expect(excon).to receive(:post) do |args|
235
+ captured_block = args[:request_block]
236
+ double(status: 201, body: '')
237
+ end
238
+
239
+ ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)
240
+
241
+ # Execute the block and verify the fake response is passed to the handler
242
+ expect(handler).to receive(:call).with(an_instance_of(Ollama::Response))
243
+ captured_block.call
244
+ end
245
+
246
+ it 'can raise error on non-success status' do
247
+ body = StringIO.new('dummy content')
248
+ handler = double('Handler', call: nil)
249
+
250
+ expect(excon).to receive(:post).and_return(double(status: 400, body: 'Bad Request'))
251
+
252
+ expect {
253
+ ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)
254
+ }.to raise_error(Ollama::Errors::Error, /400 "Bad Request"/)
255
+ end
256
+
257
+ it 'can map Excon socket errors to Ollama::Errors::SocketError' do
258
+ body = StringIO.new('dummy content')
259
+ handler = double('Handler', call: nil)
260
+
261
+ expect(excon).to receive(:post).and_raise(Excon::Errors::SocketError)
262
+
263
+ expect {
264
+ ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)
265
+ }.to raise_error(Ollama::Errors::SocketError)
266
+ end
267
+
268
+ it 'can map Excon timeout errors to Ollama::Errors::TimeoutError' do
269
+ body = StringIO.new('dummy content')
270
+ handler = double('Handler', call: nil)
271
+
272
+ expect(excon).to receive(:post).and_raise(Excon::Errors::Timeout)
273
+
274
+ expect {
275
+ ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)
276
+ }.to raise_error(Ollama::Errors::TimeoutError)
277
+ end
278
+
279
+ it 'can map generic Excon errors to Ollama::Errors::Error' do
280
+ body = StringIO.new('dummy content')
281
+ handler = double('Handler', call: nil)
282
+
283
+ expect(excon).to receive(:post).and_raise(Excon::Error)
284
+
285
+ expect {
286
+ ollama.upload_file(path: '/api/blobs/abc', body: body, handler: handler)
287
+ }.to raise_error(Ollama::Errors::Error)
288
+ end
289
+ end
196
290
  end
@@ -0,0 +1,49 @@
1
+ describe Ollama::Commands::BlobExists do
2
+ let :hex_digest do
3
+ '5ee4f07cdb9beadbbb293e85803c569b01bd37ed059d2715faa7bb405f31caa6'
4
+ end
5
+
6
+ let :digest do
7
+ "sha256:#{hex_digest}"
8
+ end
9
+
10
+ let :blob_exists do
11
+ described_class.new(digest:)
12
+ end
13
+
14
+ it 'can be instantiated with a full digest' do
15
+ expect(blob_exists).to be_a described_class
16
+ expect(blob_exists.digest).to eq digest
17
+ end
18
+
19
+ it 'automatically prefixes 64-char hex digests' do
20
+ be = described_class.new(digest: hex_digest)
21
+ expect(be.digest).to eq "sha256:#{hex_digest}"
22
+ end
23
+
24
+ it 'computes digest when provided with a blob' do
25
+ blob = StringIO.new('hello world')
26
+ be = described_class.new(blob: blob)
27
+ expect(be.digest).to start_with('sha256:')
28
+ expect(be.digest).not_to be_nil
29
+ end
30
+
31
+ it 'raises ArgumentError if neither digest nor blob is provided' do
32
+ expect { described_class.new }.to\
33
+ raise_error(ArgumentError, 'require digest or blob to perform')
34
+ end
35
+
36
+ it 'cannot be converted to JSON' do
37
+ expect(blob_exists).not_to respond_to(:as_json)
38
+ end
39
+
40
+ it 'can perform' do
41
+ blob_exists.client = ollama = double('Ollama::Client')
42
+ expect(ollama).to receive(:request).
43
+ with(
44
+ method: :head, path: "/api/blobs/#{digest}", handler: Ollama::Handlers::NOP,
45
+ stream: false
46
+ )
47
+ blob_exists.perform(Ollama::Handlers::NOP)
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ describe Ollama::Commands::PushBlob do
2
+ let :body do
3
+ 'test blob content'
4
+ end
5
+
6
+ let :push_blob do
7
+ described_class.new(body: body)
8
+ end
9
+
10
+ it 'can be instantiated with a string' do
11
+ expect(push_blob).to be_a described_class
12
+ end
13
+
14
+ it 'can be instantiated with an IO object' do
15
+ io = StringIO.new(body)
16
+ push_blob_io = described_class.new(body: io)
17
+ expect(push_blob_io).to be_a described_class
18
+ end
19
+
20
+ it 'cannot be converted to JSON' do
21
+ expect(push_blob).not_to respond_to(:as_json)
22
+ end
23
+
24
+ it 'calculates the correct digest and path' do
25
+ expected_digest = 'sha256:dccfe42873d40807d0da4be11f3a412e4914f1315288d3c6e8cf0a19a8928feb'
26
+ expect(push_blob.digest).to eq expected_digest
27
+ expect(push_blob.path).to eq "/api/blobs/#{expected_digest}"
28
+ end
29
+
30
+ it 'can perform' do
31
+ handler = double('Ollama::Handlers::NOP').as_null_object
32
+ push_blob.client = ollama = double('Ollama::Client')
33
+
34
+ expect(handler).to receive(:result=).with(push_blob.digest)
35
+ expect(ollama).to receive(:blob_exists?).and_return false
36
+ expect(ollama).to receive(:upload_file).with(
37
+ path: push_blob.path, body: push_blob.body, handler: handler
38
+ )
39
+
40
+ push_blob.perform(handler)
41
+ end
42
+ end
@@ -18,7 +18,7 @@ describe Ollama::Commands::Show do
18
18
  show = described_class.new(model: 'llama3.1')
19
19
  show.client = ollama = double('Ollama::Client')
20
20
  expect(ollama).to receive(:request).with(
21
- method: :post, path: '/api/show', handler: Ollama::Handlers::NOP ,stream: false,
21
+ method: :post, path: '/api/show', handler: Ollama::Handlers::NOP, stream: false,
22
22
  body: '{"model":"llama3.1","stream":false}'
23
23
  )
24
24
  show.perform(Ollama::Handlers::NOP)
@@ -0,0 +1,62 @@
1
+ require 'stringio'
2
+
3
+ describe Ollama::Digester do
4
+ # Since compute_digest is private, we create a test class to include it
5
+ let :test_class do
6
+ Class.new do
7
+ include Ollama::Digester
8
+
9
+ def digest(io, **args)
10
+ compute_digest(io, **args)
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:digester) { test_class.new }
16
+
17
+ let(:content) { 'Hello, Ruby happiness! 🍓' }
18
+
19
+ let(:io) { StringIO.new(content) }
20
+
21
+ describe '#compute_digest' do
22
+ it 'calculates the correct SHA256 digest with the required prefix' do
23
+ expected_hash = "sha256:#{Digest::SHA256.hexdigest(content)}"
24
+ expect(digester.digest(io)).to eq(expected_hash)
25
+ end
26
+
27
+ it 'rewinds the IO object before starting if it is not at the beginning' do
28
+ io.seek(content.length / 2) # Move pointer to the middle
29
+
30
+ # If it didn't rewind, it would only hash the second half
31
+ expected_hash = "sha256:#{Digest::SHA256.hexdigest(content)}"
32
+ expect(digester.digest(io)).to eq(expected_hash)
33
+ end
34
+
35
+ it 'leaves the IO object rewound after processing' do
36
+ digester.digest(io)
37
+ expect(io.pos).to eq(0)
38
+ end
39
+
40
+ it 'works correctly when using a very small chunk size' do
41
+ # Force many iterations of the loop by using 1-byte chunks
42
+ expected_hash = "sha256:#{Digest::SHA256.hexdigest(content)}"
43
+ expect(digester.digest(io, chunk_size: 1)).to eq(expected_hash)
44
+ end
45
+
46
+ it 'handles empty streams correctly' do
47
+ empty_io = StringIO.new('')
48
+ expected_hash = "sha256:#{Digest::SHA256.hexdigest('')}"
49
+ expect(digester.digest(empty_io)).to eq(expected_hash)
50
+ end
51
+
52
+ it 'ensures the IO is rewound even if an error occurs during hashing' do
53
+ io.seek(content.length / 2) # Move pointer to the middle
54
+
55
+ # Mock Digest to raise an error
56
+ allow(Digest::SHA256).to receive(:new).and_raise(StandardError, 'Hashing failed')
57
+
58
+ expect { digester.digest(io) }.to raise_error(StandardError, 'Hashing failed')
59
+ expect(io.pos).to eq(0)
60
+ end
61
+ end
62
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ollama-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.21.0
4
+ version: 1.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 2.17.0
18
+ version: 2.17.1
19
19
  type: :development
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 2.17.0
25
+ version: 2.17.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: all_images
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -269,6 +269,7 @@ extra_rdoc_files:
269
269
  - lib/ollama/client/command.rb
270
270
  - lib/ollama/client/configuration/config.rb
271
271
  - lib/ollama/client/doc.rb
272
+ - lib/ollama/commands/blob_exists.rb
272
273
  - lib/ollama/commands/chat.rb
273
274
  - lib/ollama/commands/copy.rb
274
275
  - lib/ollama/commands/create.rb
@@ -279,9 +280,11 @@ extra_rdoc_files:
279
280
  - lib/ollama/commands/ps.rb
280
281
  - lib/ollama/commands/pull.rb
281
282
  - lib/ollama/commands/push.rb
283
+ - lib/ollama/commands/push_blob.rb
282
284
  - lib/ollama/commands/show.rb
283
285
  - lib/ollama/commands/tags.rb
284
286
  - lib/ollama/commands/version.rb
287
+ - lib/ollama/digester.rb
285
288
  - lib/ollama/dto.rb
286
289
  - lib/ollama/errors.rb
287
290
  - lib/ollama/handlers.rb
@@ -323,6 +326,7 @@ files:
323
326
  - lib/ollama/client/command.rb
324
327
  - lib/ollama/client/configuration/config.rb
325
328
  - lib/ollama/client/doc.rb
329
+ - lib/ollama/commands/blob_exists.rb
326
330
  - lib/ollama/commands/chat.rb
327
331
  - lib/ollama/commands/copy.rb
328
332
  - lib/ollama/commands/create.rb
@@ -333,9 +337,11 @@ files:
333
337
  - lib/ollama/commands/ps.rb
334
338
  - lib/ollama/commands/pull.rb
335
339
  - lib/ollama/commands/push.rb
340
+ - lib/ollama/commands/push_blob.rb
336
341
  - lib/ollama/commands/show.rb
337
342
  - lib/ollama/commands/tags.rb
338
343
  - lib/ollama/commands/version.rb
344
+ - lib/ollama/digester.rb
339
345
  - lib/ollama/dto.rb
340
346
  - lib/ollama/errors.rb
341
347
  - lib/ollama/handlers.rb
@@ -365,6 +371,7 @@ files:
365
371
  - spec/assets/options.json
366
372
  - spec/ollama/client/doc_spec.rb
367
373
  - spec/ollama/client_spec.rb
374
+ - spec/ollama/commands/blob_exists_spec.rb
368
375
  - spec/ollama/commands/chat_spec.rb
369
376
  - spec/ollama/commands/copy_spec.rb
370
377
  - spec/ollama/commands/create_spec.rb
@@ -374,10 +381,12 @@ files:
374
381
  - spec/ollama/commands/generate_spec.rb
375
382
  - spec/ollama/commands/ps_spec.rb
376
383
  - spec/ollama/commands/pull_spec.rb
384
+ - spec/ollama/commands/push_blob_spec.rb
377
385
  - spec/ollama/commands/push_spec.rb
378
386
  - spec/ollama/commands/show_spec.rb
379
387
  - spec/ollama/commands/tags_spec.rb
380
388
  - spec/ollama/commands/version_spec.rb
389
+ - spec/ollama/digester_spec.rb
381
390
  - spec/ollama/handlers/collector_spec.rb
382
391
  - spec/ollama/handlers/dump_json_spec.rb
383
392
  - spec/ollama/handlers/dump_yaml_spec.rb
@@ -415,12 +424,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
415
424
  - !ruby/object:Gem::Version
416
425
  version: '0'
417
426
  requirements: []
418
- rubygems_version: 4.0.8
427
+ rubygems_version: 4.0.10
419
428
  specification_version: 4
420
429
  summary: Interacting with the Ollama API
421
430
  test_files:
422
431
  - spec/ollama/client/doc_spec.rb
423
432
  - spec/ollama/client_spec.rb
433
+ - spec/ollama/commands/blob_exists_spec.rb
424
434
  - spec/ollama/commands/chat_spec.rb
425
435
  - spec/ollama/commands/copy_spec.rb
426
436
  - spec/ollama/commands/create_spec.rb
@@ -430,10 +440,12 @@ test_files:
430
440
  - spec/ollama/commands/generate_spec.rb
431
441
  - spec/ollama/commands/ps_spec.rb
432
442
  - spec/ollama/commands/pull_spec.rb
443
+ - spec/ollama/commands/push_blob_spec.rb
433
444
  - spec/ollama/commands/push_spec.rb
434
445
  - spec/ollama/commands/show_spec.rb
435
446
  - spec/ollama/commands/tags_spec.rb
436
447
  - spec/ollama/commands/version_spec.rb
448
+ - spec/ollama/digester_spec.rb
437
449
  - spec/ollama/handlers/collector_spec.rb
438
450
  - spec/ollama/handlers/dump_json_spec.rb
439
451
  - spec/ollama/handlers/dump_yaml_spec.rb