ollama-ruby 0.9.3 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fda5b55631024d2e7367400032462d87244daaa44dac2835aae7f7dad3f3f885
4
- data.tar.gz: b085138d8de3daa61cc5f7f2cdfc17ff14c7a17737675452ce8e9ea5be002113
3
+ metadata.gz: 6611c14b779a919b256552774080305825b4e81cd5e43ae0303026aaaed7c13e
4
+ data.tar.gz: a34e71ef5b07cdd8a0cdf5dce9de7f7e9c3faca1829e4837c54ea243458b8f05
5
5
  SHA512:
6
- metadata.gz: dc3c61c2f9484d5d2a5e8b5fc97c357139d8326700ea83e6b61984c7b58d62fe2377a5c288a65a6aad7c98dcf4b3bb341772f0ff4fd9bf3b3bf9b23061040eb1
7
- data.tar.gz: 4c14196cea496a866f50bd1e414620bc7441a1923a729cffe52a42ef8cc77b16982b4be2bc644b09601900d5b403b117dd3dfe462dd5d4c6e1f324699d7396d8
6
+ metadata.gz: aa38db4bdd42ebffe2ef9b5abf7f644e38821acc4fb9650b7e8646a9fab3350640224c4ba59e13d7f182becd2a84ac881fd0c5d136ca88782e1a7febec31c2fe
7
+ data.tar.gz: b14f2626d9f2c8e6e8bc6c3b3d8947d78b4400cf0dd8888cf9f818261ba3605fc9971c2c9015f976ec48f2004cb4b0ff5d922e2efe001b5b443f03728f14ce51
data/CHANGES.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changes
2
2
 
3
+ ## 2024-11-20 v0.11.0
4
+
5
+ * Added `voice` and `interactive` reader attributes to the Say handler class.
6
+ * Refactored the `call` method in the Say handler to reopen the output stream
7
+ if it has been closed.
8
+ * Added the `open_output` method to open a new IO stream with synchronization
9
+ enabled.
10
+ * Added a test for the reopened output stream in the Say spec.
11
+ * Updated `initialize` method in `lib/ollama/handlers/say.rb` to add
12
+ `interactive` option and call new `command` method.
13
+ * Add private `command` method in lib/ollama/handlers/say.rb to generate
14
+ command for say utility based on voice and interactive options.
15
+ * Update specs in `spec/ollama/handlers/say_spec.rb` to test new behavior.
16
+ * Updated `FollowChat` class to correctly initialize markdown and voice
17
+ attributes
18
+ * Update `choose_document_policy` policy list in chat script to include
19
+ 'ignoring'
20
+ * Updated `parse_content` method to handle 'ignoring' document policy.
21
+
22
+ ## 2024-10-31 v0.10.0
23
+
24
+ * Improved URL and tag parsing in `parse_content`:
25
+ + Added support for `file://` protocol to content scans.
26
+ + Updated regex pattern to match local files starting with `~`, `.`, or `/`.
27
+ + Remove # anchors for file URLs (and files)
28
+ * Improved parsing of content in ollama_chat:
29
+ + Use `content.scan(%r((https?://\S+)|(#\S+)|(\S+\/\S+)))` to match URLs, tags and files.
30
+ + For foo/bar file pathes prepend `./`foo/bar, for foo you have to enter ./foo still.
31
+ + Added a check for file existence before fetching its content
32
+ * Move vector methods into cache implementations:
33
+ + Update `documents.rb` to use `@cache.norm` and `@cache.cosine_similarity`
34
+ + Remove unused code in `documents.rb` (including `Ollama::Utils::Math`)
35
+ + Add `include Ollama::Utils::Math` in `cache/common.rb`
36
+ * Document existing collections, pre, and unpre methods:
37
+ + Update module Ollama::Documents::Cache::Common to include documentation for existing methods
38
+ * Use kramdown-ansi gem for Markdown output in terminal:
39
+ + Update `kramdown-ansi` to **0.0** in Gemfile and Rakefile
40
+ + Remove `ollama/utils/width.rb` as it's no longer needed
41
+ + Remove `spec/ollama/utils/width_spec.rb` and `spec/ollama/utils/ansi_markdown_spec.rb` as they're not used anymore
42
+ + Update `ollama-ruby.gemspec` to reflect the new dependencies and remove unused files
43
+ * Updated print messages to include `.to_s.inspect` for accurate inspection of
44
+ source content in `ollama_chat`.
45
+
3
46
  ## 2024-10-21 v0.9.3
4
47
 
5
48
  * Update dependencies and date:
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ GemHadar do
14
14
  test_dir 'spec'
15
15
  ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
16
16
  '.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
17
- 'corpus'
17
+ 'corpus', 'yard'
18
18
  package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
19
19
  '.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
20
20
  readme 'README.md'
@@ -24,11 +24,9 @@ GemHadar do
24
24
 
25
25
  required_ruby_version '~> 3.1'
26
26
 
27
- dependency 'excon', '~> 0.111'
27
+ dependency 'excon', '~> 1.0'
28
28
  dependency 'infobar', '~> 0.8'
29
29
  dependency 'term-ansicolor', '~> 1.11'
30
- dependency 'kramdown-parser-gfm', '~> 1.1'
31
- dependency 'terminal-table', '~> 3.0'
32
30
  dependency 'redis', '~> 5.0'
33
31
  dependency 'numo-narray', '~> 0.9'
34
32
  dependency 'more_math', '~> 1.1'
@@ -42,6 +40,8 @@ GemHadar do
42
40
  dependency 'json', '~> 2.0'
43
41
  dependency 'xdg', '~> 7.0'
44
42
  dependency 'tins', '~> 1.34'
43
+ dependency 'kramdown-ansi', '~> 0.0', '>= 0.0.1'
44
+ dependency 'ostruct', '~> 0.0'
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
@@ -114,10 +114,10 @@ class FollowChat
114
114
  def initialize(messages:, markdown: false, voice: nil, output: $stdout)
115
115
  super(output:)
116
116
  @output.sync = true
117
- @markdown = markdown
118
- @say = voice ? Handlers::Say.new(voice:) : NOP
119
- @messages = messages
120
- @user = nil
117
+ @markdown = markdown
118
+ @say = voice ? Handlers::Say.new(voice:) : NOP
119
+ @messages = messages
120
+ @user = nil
121
121
  end
122
122
 
123
123
  def call(response)
@@ -132,7 +132,7 @@ class FollowChat
132
132
  content = response.message&.content
133
133
  @messages.last.content << content
134
134
  if @markdown and content = @messages.last.content.full?
135
- markdown_content = Utils::ANSIMarkdown.parse(content)
135
+ markdown_content = Kramdown::ANSI.parse(content)
136
136
  @output.print clear_screen, move_home, @user, ?\n, markdown_content
137
137
  else
138
138
  @output.print content
@@ -159,7 +159,7 @@ class FollowChat
159
159
  load_duration: Tins::Duration.new(response.load_duration / 1e9),
160
160
  }.map { _1 * '=' } * ' '
161
161
  '📊 ' + color(111) {
162
- Utils::Width.wrap(stats_text, percentage: 90).gsub(/(?<!\A)^/, ' ')
162
+ Kramdown::ANSI::Width.wrap(stats_text, percentage: 90).gsub(/(?<!\A)^/, ' ')
163
163
  }
164
164
  end
165
165
  end
@@ -364,7 +364,7 @@ def list_conversation(messages, last = nil)
364
364
  when 'system' then 213
365
365
  else 210
366
366
  end
367
- content = m.content.full? { $markdown.on? ? Utils::ANSIMarkdown.parse(_1) : _1 }
367
+ content = m.content.full? { $markdown.on? ? Kramdown::ANSI.parse(_1) : _1 }
368
368
  message_text = message_type(m.images) + " "
369
369
  message_text += bold { color(role_color) { m.role } }
370
370
  message_text += ":\n#{content}"
@@ -539,8 +539,8 @@ end
539
539
 
540
540
  def import_source(source_io, source)
541
541
  source = source.to_s
542
- puts "Importing #{italic { source_io&.content_type }} document #{source.inspect} now."
543
- source_content = parse_source(source_io)
542
+ puts "Importing #{italic { source_io&.content_type }} document #{source.to_s.inspect} now."
543
+ source_content = parse_source(source_io)
544
544
  "Imported #{source.inspect}:\n#{source_content}\n\n"
545
545
  end
546
546
 
@@ -553,7 +553,7 @@ def import(source)
553
553
  end
554
554
 
555
555
  def summarize_source(source_io, source, words: nil)
556
- puts "Summarizing #{italic { source_io&.content_type }} document #{source.inspect} now."
556
+ puts "Summarizing #{italic { source_io&.content_type }} document #{source.to_s.inspect} now."
557
557
  words = words.to_i
558
558
  words < 1 and words = 100
559
559
  source_content = parse_source(source_io)
@@ -608,7 +608,7 @@ def embed_source(source_io, source, count: nil)
608
608
  inputs or return
609
609
  source = source.to_s
610
610
  if source.start_with?(?!)
611
- source = Utils::Width.truncate(
611
+ source = Kramdown::ANSI::Width.truncate(
612
612
  source[1..-1].gsub(/\W+/, ?_),
613
613
  length: 10
614
614
  )
@@ -638,31 +638,39 @@ def parse_content(content, images)
638
638
  tags = Utils::Tags.new
639
639
 
640
640
  contents = [ content ]
641
- content.scan(%r((?:\.\.|[.~])?/\S+|https?://\S+|#\S+)).each do |source|
642
- case source
643
- when /\A#(\S+)/
644
- tags.add($1, source:)
645
- else
646
- source = source.sub(/(["')]|\*+)\z/, '')
647
- fetch_source(source) do |source_io|
648
- case source_io&.content_type&.media_type
649
- when 'image'
650
- add_image(images, source_io, source)
651
- when 'text', 'application', nil
652
- case $document_policy
653
- when 'importing'
654
- contents << import_source(source_io, source)
655
- when 'embedding'
656
- embed_source(source_io, source)
657
- when 'summarizing'
658
- contents << summarize_source(source_io, source)
659
- end
660
- else
661
- STDERR.puts(
662
- "Cannot fetch #{source.to_s.inspect} with content type "\
663
- "#{source_io&.content_type.inspect}"
664
- )
641
+ content.scan(%r((https?://\S+)|(#\S+)|(?:file://)?(\S*\/\S+))).each do |url, tag, file|
642
+ case
643
+ when tag
644
+ tags.add(tag)
645
+ next
646
+ when file
647
+ file = file.sub(/#.*/, '')
648
+ file =~ %r(\A[~./]) or file.prepend('./')
649
+ File.exist?(file) or next
650
+ source = file
651
+ when url
652
+ source = url
653
+ end
654
+ fetch_source(source) do |source_io|
655
+ case source_io&.content_type&.media_type
656
+ when 'image'
657
+ add_image(images, source_io, source)
658
+ when 'text', 'application', nil
659
+ case $document_policy
660
+ when 'ignoring'
661
+ nil
662
+ when 'importing'
663
+ contents << import_source(source_io, source)
664
+ when 'embedding'
665
+ embed_source(source_io, source)
666
+ when 'summarizing'
667
+ contents << summarize_source(source_io, source)
665
668
  end
669
+ else
670
+ STDERR.puts(
671
+ "Cannot fetch #{source.to_s.inspect} with content type "\
672
+ "#{source_io&.content_type.inspect}"
673
+ )
666
674
  end
667
675
  end
668
676
  end
@@ -705,7 +713,7 @@ ensure
705
713
  end
706
714
 
707
715
  def choose_document_policy
708
- policies = %w[ importing embedding summarizing ].sort
716
+ policies = %w[ importing embedding summarizing ignoring ].sort
709
717
  current = if policies.index($document_policy)
710
718
  $document_policy
711
719
  elsif policies.index($config.document_policy)
@@ -755,7 +763,7 @@ end
755
763
  def show_system_prompt
756
764
  puts <<~EOT
757
765
  Configured system prompt is:
758
- #{Utils::ANSIMarkdown.parse($system.to_s).gsub(/\n+\z/, '').full? || 'n/a'}
766
+ #{Kramdown::ANSI.parse($system.to_s).gsub(/\n+\z/, '').full? || 'n/a'}
759
767
  EOT
760
768
  end
761
769
 
@@ -1165,7 +1173,8 @@ loop do
1165
1173
  end
1166
1174
 
1167
1175
  if location = at_location.full?
1168
- content += " [#{location} – do not comment on this information, just consider it for eventual queries]"
1176
+ content += " [#{location} – do not comment on this the time and location, "\
1177
+ "just consider it for eventual queries]"
1169
1178
  end
1170
1179
 
1171
1180
  messages << Message.new(role: 'user', content:, images: images.dup)
@@ -1,16 +1,31 @@
1
1
  module Ollama::Documents::Cache::Common
2
- attr_writer :prefix
2
+ include Ollama::Utils::Math
3
3
 
4
+ attr_writer :prefix # current prefix defined for the cache
5
+
6
+ # Returns an array of collection names that match the given prefix.
7
+ #
8
+ # @param prefix [String] a string to search for in collection names
9
+ # @return [Array<Symbol>] an array of matching collection names
4
10
  def collections(prefix)
5
11
  unique = Set.new
6
12
  full_each { |key, _| unique << key[/\A#{prefix}(.*)-/, 1] }
7
13
  unique.map(&:to_sym)
8
14
  end
9
15
 
16
+ # Returns a string representing the given `key` prefixed with the defined
17
+ # prefix.
18
+ #
19
+ # @param key [String] the key to join with the prefix
20
+ # @return [String] the joined string of prefix and key
10
21
  def pre(key)
11
22
  [ @prefix, key ].join
12
23
  end
13
24
 
25
+ # Returns a string with the prefix removed from the given `key`.
26
+ #
27
+ # @param key [String] the input string containing the prefix.
28
+ # @return [String] the input string without the prefix.
14
29
  def unpre(key)
15
30
  key.sub(/\A#@prefix/, '')
16
31
  end
@@ -1,5 +1,6 @@
1
1
  require 'numo/narray'
2
2
  require 'digest'
3
+ require 'kramdown/ansi'
3
4
 
4
5
  class Ollama::Documents
5
6
  end
@@ -14,8 +15,7 @@ require 'ollama/documents/splitters/character'
14
15
  require 'ollama/documents/splitters/semantic'
15
16
 
16
17
  class Ollama::Documents
17
- include Ollama::Utils::Math
18
- include Ollama::Utils::Width
18
+ include Kramdown::ANSI::Width
19
19
 
20
20
  class Record < JSON::GenericObject
21
21
  def to_s
@@ -79,7 +79,7 @@ class Ollama::Documents
79
79
  batches.each do |batch|
80
80
  embeddings = fetch_embeddings(model:, options: @model_options, input: batch)
81
81
  batch.zip(embeddings) do |text, embedding|
82
- norm = norm(embedding)
82
+ norm = @cache.norm(embedding)
83
83
  self[text] = Record[text:, embedding:, norm:, source:, tags: tags.to_a]
84
84
  end
85
85
  infobar.progress by: batch.size
@@ -125,7 +125,7 @@ class Ollama::Documents
125
125
 
126
126
  def find(string, tags: nil, prompt: nil)
127
127
  needle = convert_to_vector(string, prompt:)
128
- needle_norm = norm(needle)
128
+ needle_norm = @cache.norm(needle)
129
129
  records = @cache
130
130
  if tags
131
131
  tags = Ollama::Utils::Tags.new(tags).to_a
@@ -133,7 +133,7 @@ class Ollama::Documents
133
133
  end
134
134
  records = records.sort_by { |key, record|
135
135
  record.key = key
136
- record.similarity = cosine_similarity(
136
+ record.similarity = @cache.cosine_similarity(
137
137
  a: needle,
138
138
  b: record.embedding,
139
139
  a_norm: needle_norm,
@@ -193,11 +193,8 @@ class Ollama::Documents
193
193
  if prompt
194
194
  input = prompt % input
195
195
  end
196
- if input.is_a?(String)
197
- Numo::NArray[*fetch_embeddings(model:, input:).first]
198
- else
199
- super(input)
200
- end
196
+ input.is_a?(String) and input = fetch_embeddings(model:, input:).first
197
+ @cache.convert_to_vector(input)
201
198
  end
202
199
 
203
200
  def fetch_embeddings(model:, input:, options: nil)
@@ -1,4 +1,5 @@
1
1
  require 'term/ansicolor'
2
+ require 'kramdown/ansi'
2
3
 
3
4
  class Ollama::Handlers::Markdown
4
5
  include Ollama::Handlers::Concern
@@ -13,7 +14,7 @@ class Ollama::Handlers::Markdown
13
14
  def call(response)
14
15
  if content = response.response || response.message&.content
15
16
  @content << content
16
- markdown_content = Ollama::Utils::ANSIMarkdown.parse(@content)
17
+ markdown_content = Kramdown::ANSI.parse(@content)
17
18
  @output.print clear_screen, move_home, markdown_content
18
19
  end
19
20
  self
@@ -3,17 +3,56 @@ require 'shellwords'
3
3
  class Ollama::Handlers::Say
4
4
  include Ollama::Handlers::Concern
5
5
 
6
- def initialize(output: nil, voice: 'Samantha')
7
- output ||= IO.popen(Shellwords.join([ 'say', '-v', voice ]), 'w')
6
+ def initialize(output: nil, voice: 'Samantha', interactive: nil)
7
+ @voice = voice
8
+ @interactive = interactive
8
9
  super(output:)
9
- @output.sync = true
10
+ unless output
11
+ @output = open_output
12
+ @output_pid = @output.pid
13
+ end
10
14
  end
11
15
 
16
+ attr_reader :voice
17
+
18
+ attr_reader :interactive
19
+
12
20
  def call(response)
21
+ if @output.closed?
22
+ wait_output_pid
23
+ @output = open_output
24
+ @output_pid = @output.pid
25
+ end
13
26
  if content = response.response || response.message&.content
14
27
  @output.print content
15
28
  end
16
29
  response.done and @output.close
17
30
  self
18
31
  end
32
+
33
+ private
34
+
35
+ def open_output
36
+ io = IO.popen(Shellwords.join(command(voice:, interactive:)), 'w')
37
+ io.sync = true
38
+ io
39
+ end
40
+
41
+ def wait_output_pid
42
+ @output_pid or return
43
+ Process.wait(@output_pid, Process::WNOHANG | Process::WUNTRACED)
44
+ rescue Errno::ECHILD
45
+ end
46
+
47
+ def command(voice:, interactive:)
48
+ command = [ 'say' ]
49
+ voice and command.concat([ '-v', voice ])
50
+ case interactive
51
+ when true
52
+ command << '-i'
53
+ when String
54
+ command << '--interactive=%s' % interactive
55
+ end
56
+ command
57
+ end
19
58
  end
@@ -1,7 +1,10 @@
1
+ require 'term/ansicolor'
2
+ require 'kramdown/ansi'
3
+
1
4
  class Ollama::Utils::ColorizeTexts
2
5
  include Math
3
6
  include Term::ANSIColor
4
- include Ollama::Utils::Width
7
+ include Kramdown::ANSI::Width
5
8
 
6
9
  # Initializes a new instance of Ollama::Utils::ColorizeTexts
7
10
  #
@@ -1,6 +1,6 @@
1
1
  module Ollama
2
2
  # Ollama version
3
- VERSION = '0.9.3'
3
+ VERSION = '0.11.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
@@ -16,8 +16,6 @@ end
16
16
 
17
17
  module Ollama::Utils
18
18
  end
19
- require 'ollama/utils/width'
20
- require 'ollama/utils/ansi_markdown'
21
19
  require 'ollama/utils/tags'
22
20
  require 'ollama/utils/math'
23
21
  require 'ollama/utils/colorize_texts'
data/ollama-ruby.gemspec CHANGED
@@ -1,26 +1,26 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama-ruby 0.9.3 ruby lib
2
+ # stub: ollama-ruby 0.11.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama-ruby".freeze
6
- s.version = "0.9.3".freeze
6
+ s.version = "0.11.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-10-21"
11
+ s.date = "2024-11-20"
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
- 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/colorize_texts_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]
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/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/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/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/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/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/colorize_texts_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]
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.22".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/colorize_texts_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]
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/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/colorize_texts_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]
24
24
 
25
25
  s.specification_version = 4
26
26
 
@@ -30,11 +30,9 @@ Gem::Specification.new do |s|
30
30
  s.add_development_dependency(%q<webmock>.freeze, [">= 0".freeze])
31
31
  s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
32
32
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
33
- s.add_runtime_dependency(%q<excon>.freeze, ["~> 0.111".freeze])
33
+ s.add_runtime_dependency(%q<excon>.freeze, ["~> 1.0".freeze])
34
34
  s.add_runtime_dependency(%q<infobar>.freeze, ["~> 0.8".freeze])
35
35
  s.add_runtime_dependency(%q<term-ansicolor>.freeze, ["~> 1.11".freeze])
36
- s.add_runtime_dependency(%q<kramdown-parser-gfm>.freeze, ["~> 1.1".freeze])
37
- s.add_runtime_dependency(%q<terminal-table>.freeze, ["~> 3.0".freeze])
38
36
  s.add_runtime_dependency(%q<redis>.freeze, ["~> 5.0".freeze])
39
37
  s.add_runtime_dependency(%q<numo-narray>.freeze, ["~> 0.9".freeze])
40
38
  s.add_runtime_dependency(%q<more_math>.freeze, ["~> 1.1".freeze])
@@ -48,4 +46,6 @@ Gem::Specification.new do |s|
48
46
  s.add_runtime_dependency(%q<json>.freeze, ["~> 2.0".freeze])
49
47
  s.add_runtime_dependency(%q<xdg>.freeze, ["~> 7.0".freeze])
50
48
  s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.34".freeze])
49
+ s.add_runtime_dependency(%q<kramdown-ansi>.freeze, ["~> 0.0".freeze, ">= 0.0.1".freeze])
50
+ s.add_runtime_dependency(%q<ostruct>.freeze, ["~> 0.0".freeze])
51
51
  end
@@ -1,30 +1,93 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Ollama::Handlers::Say do
4
+ let :say do
5
+ described_class.new
6
+ end
7
+
4
8
  it 'has .to_proc' do
5
9
  expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
10
  described_class.call(:foo)
7
11
  end
8
12
 
9
- it 'can print response' do
10
- output = double('output', :sync= => true)
13
+ it 'can be instantiated' do
14
+ expect(say).to be_a described_class
15
+ end
16
+
17
+ it 'can be instantiated with a given voice' do
18
+ expect_any_instance_of(described_class).to receive(:command).
19
+ with(hash_including(voice: 'TheVoice')).and_return %w[ true ]
20
+ say = described_class.new(voice: 'TheVoice')
21
+ expect(say).to be_a described_class
22
+ end
23
+
24
+ describe 'command' do
25
+ it 'can be instantiated interactively' do
26
+ expect_any_instance_of(described_class).to receive(:command).
27
+ with(hash_including(interactive: true)).and_return %w[ true ]
28
+ say = described_class.new(interactive: true)
29
+ expect(say).to be_a described_class
30
+ end
31
+
32
+ it 'can set the voice' do
33
+ expect(say.send(:command, voice: 'TheVoice', interactive: nil)).to eq(
34
+ %w[ say -v TheVoice ]
35
+ )
36
+ end
37
+
38
+ it 'can be instantiated interactively with green' do
39
+ expect_any_instance_of(described_class).to receive(:command).
40
+ with(hash_including(interactive: 'green')).and_return %w[ true ]
41
+ say = described_class.new(interactive: 'green')
42
+ expect(say).to be_a described_class
43
+ end
44
+
45
+ it 'can set interactive mode' do
46
+ expect(say.send(:command, voice: nil, interactive: true)).to eq(
47
+ %w[ say -i ]
48
+ )
49
+ end
50
+
51
+ it 'can set interactive mode to green' do
52
+ expect(say.send(:command, voice: nil, interactive: 'green')).to eq(
53
+ %w[ say --interactive=green ]
54
+ )
55
+ end
56
+ end
57
+
58
+ it 'can say response' do
59
+ output = double('output', :sync= => true, closed?: false)
11
60
  expect(output).to receive(:print).with('testing')
12
61
  expect(output).to receive(:close)
13
- print = described_class.new(output:)
62
+ say = described_class.new(output:)
14
63
  response = double('response', response: 'testing', done: false)
15
- print.call(response)
64
+ say.call(response)
16
65
  response = double('response', response: nil, message: nil, done: true)
17
- print.call(response)
66
+ say.call(response)
18
67
  end
19
68
 
20
- it 'can print message content' do
21
- output = double('output', :sync= => true)
69
+ it 'can say message content' do
70
+ output = double('output', :sync= => true, closed?: false)
22
71
  expect(output).to receive(:print).with('testing')
23
72
  expect(output).to receive(:close)
24
- print = described_class.new(output:)
73
+ say = described_class.new(output:)
25
74
  response = double('response', response: nil, message: double(content: 'testing'), done: false)
26
- print.call(response)
75
+ say.call(response)
76
+ response = double('response', response: nil, message: nil, done: true)
77
+ say.call(response)
78
+ end
79
+
80
+ it 'can reopen output if closed' do
81
+ output = double('output', :sync= => true, closed?: true)
82
+ reopened_output = double('output', :sync= => true, closed?: false, pid: 666)
83
+ expect(reopened_output).to receive(:print).with('testing')
84
+ expect(reopened_output).to receive(:close)
85
+ say = described_class.new(output:)
86
+ expect(say).to receive(:open_output).and_return(reopened_output)
87
+ response = double('response', response: 'testing', done: false)
88
+ say.call(response)
27
89
  response = double('response', response: nil, message: nil, done: true)
28
- print.call(response)
90
+ say.call(response)
29
91
  end
92
+
30
93
  end
@@ -77,12 +77,12 @@ RSpec.describe Ollama::Utils::Fetcher do
77
77
  stub_request(:get, url).
78
78
  with(headers: fetcher.headers).
79
79
  to_return(status: 500)
80
- expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
81
- fetcher.get(url) do |tmp|
82
- expect(tmp).to be_a StringIO
83
- expect(tmp.read).to eq ''
84
- expect(tmp.content_type).to eq 'text/plain'
85
- end
80
+ expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
81
+ fetcher.get(url) do |tmp|
82
+ expect(tmp).to be_a StringIO
83
+ expect(tmp.read).to eq ''
84
+ expect(tmp.content_type).to eq 'text/plain'
85
+ end
86
86
  end
87
87
 
88
88
  it 'can redirect' do
@@ -107,7 +107,7 @@ RSpec.describe Ollama::Utils::Fetcher do
107
107
 
108
108
  it 'can .execute and fail' do
109
109
  expect(IO).to receive(:popen).and_raise StandardError
110
- expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
110
+ expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
111
111
  described_class.execute('foobar') do |file|
112
112
  expect(file).to be_a StringIO
113
113
  expect(file.read).to be_empty
@@ -115,23 +115,23 @@ RSpec.describe Ollama::Utils::Fetcher do
115
115
  end
116
116
  end
117
117
 
118
- describe '.normalize_url' do
119
- it 'can handle umlauts' do
120
- expect(described_class.normalize_url('https://foo.de/bär')).to eq(
121
- 'https://foo.de/b%C3%A4r'
122
- )
123
- end
118
+ describe '.normalize_url' do
119
+ it 'can handle umlauts' do
120
+ expect(described_class.normalize_url('https://foo.de/bär')).to eq(
121
+ 'https://foo.de/b%C3%A4r'
122
+ )
123
+ end
124
124
 
125
- it 'can handle escaped umlauts' do
126
- expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
127
- 'https://foo.de/b%C3%A4r'
128
- )
129
- end
125
+ it 'can handle escaped umlauts' do
126
+ expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
127
+ 'https://foo.de/b%C3%A4r'
128
+ )
129
+ end
130
130
 
131
- it 'can remove #anchors' do
132
- expect(described_class.normalize_url('https://foo.de#bar')).to eq(
133
- 'https://foo.de'
134
- )
135
- end
136
- end
131
+ it 'can remove #anchors' do
132
+ expect(described_class.normalize_url('https://foo.de#bar')).to eq(
133
+ 'https://foo.de'
134
+ )
135
+ end
136
+ end
137
137
  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.9.3
4
+ version: 0.11.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-10-21 00:00:00.000000000 Z
11
+ date: 2024-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gem_hadar
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.111'
103
+ version: '1.0'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.111'
110
+ version: '1.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: infobar
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -136,34 +136,6 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '1.11'
139
- - !ruby/object:Gem::Dependency
140
- name: kramdown-parser-gfm
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '1.1'
146
- type: :runtime
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '1.1'
153
- - !ruby/object:Gem::Dependency
154
- name: terminal-table
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '3.0'
160
- type: :runtime
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '3.0'
167
139
  - !ruby/object:Gem::Dependency
168
140
  name: redis
169
141
  requirement: !ruby/object:Gem::Requirement
@@ -352,6 +324,40 @@ dependencies:
352
324
  - - "~>"
353
325
  - !ruby/object:Gem::Version
354
326
  version: '1.34'
327
+ - !ruby/object:Gem::Dependency
328
+ name: kramdown-ansi
329
+ requirement: !ruby/object:Gem::Requirement
330
+ requirements:
331
+ - - "~>"
332
+ - !ruby/object:Gem::Version
333
+ version: '0.0'
334
+ - - ">="
335
+ - !ruby/object:Gem::Version
336
+ version: 0.0.1
337
+ type: :runtime
338
+ prerelease: false
339
+ version_requirements: !ruby/object:Gem::Requirement
340
+ requirements:
341
+ - - "~>"
342
+ - !ruby/object:Gem::Version
343
+ version: '0.0'
344
+ - - ">="
345
+ - !ruby/object:Gem::Version
346
+ version: 0.0.1
347
+ - !ruby/object:Gem::Dependency
348
+ name: ostruct
349
+ requirement: !ruby/object:Gem::Requirement
350
+ requirements:
351
+ - - "~>"
352
+ - !ruby/object:Gem::Version
353
+ version: '0.0'
354
+ type: :runtime
355
+ prerelease: false
356
+ version_requirements: !ruby/object:Gem::Requirement
357
+ requirements:
358
+ - - "~>"
359
+ - !ruby/object:Gem::Version
360
+ version: '0.0'
355
361
  description: Library that allows interacting with the Ollama API
356
362
  email: flori@ping.de
357
363
  executables:
@@ -406,7 +412,6 @@ extra_rdoc_files:
406
412
  - lib/ollama/tool/function.rb
407
413
  - lib/ollama/tool/function/parameters.rb
408
414
  - lib/ollama/tool/function/parameters/property.rb
409
- - lib/ollama/utils/ansi_markdown.rb
410
415
  - lib/ollama/utils/cache_fetcher.rb
411
416
  - lib/ollama/utils/chooser.rb
412
417
  - lib/ollama/utils/colorize_texts.rb
@@ -414,7 +419,6 @@ extra_rdoc_files:
414
419
  - lib/ollama/utils/file_argument.rb
415
420
  - lib/ollama/utils/math.rb
416
421
  - lib/ollama/utils/tags.rb
417
- - lib/ollama/utils/width.rb
418
422
  - lib/ollama/version.rb
419
423
  files:
420
424
  - ".envrc"
@@ -473,7 +477,6 @@ files:
473
477
  - lib/ollama/tool/function.rb
474
478
  - lib/ollama/tool/function/parameters.rb
475
479
  - lib/ollama/tool/function/parameters/property.rb
476
- - lib/ollama/utils/ansi_markdown.rb
477
480
  - lib/ollama/utils/cache_fetcher.rb
478
481
  - lib/ollama/utils/chooser.rb
479
482
  - lib/ollama/utils/colorize_texts.rb
@@ -481,7 +484,6 @@ files:
481
484
  - lib/ollama/utils/file_argument.rb
482
485
  - lib/ollama/utils/math.rb
483
486
  - lib/ollama/utils/tags.rb
484
- - lib/ollama/utils/width.rb
485
487
  - lib/ollama/version.rb
486
488
  - ollama-ruby.gemspec
487
489
  - spec/assets/embeddings.json
@@ -520,13 +522,11 @@ files:
520
522
  - spec/ollama/message_spec.rb
521
523
  - spec/ollama/options_spec.rb
522
524
  - spec/ollama/tool_spec.rb
523
- - spec/ollama/utils/ansi_markdown_spec.rb
524
525
  - spec/ollama/utils/cache_fetcher_spec.rb
525
526
  - spec/ollama/utils/colorize_texts_spec.rb
526
527
  - spec/ollama/utils/fetcher_spec.rb
527
528
  - spec/ollama/utils/file_argument_spec.rb
528
529
  - spec/ollama/utils/tags_spec.rb
529
- - spec/ollama/utils/width_spec.rb
530
530
  - spec/spec_helper.rb
531
531
  - tmp/.keep
532
532
  homepage: https://github.com/flori/ollama-ruby
@@ -590,11 +590,9 @@ test_files:
590
590
  - spec/ollama/message_spec.rb
591
591
  - spec/ollama/options_spec.rb
592
592
  - spec/ollama/tool_spec.rb
593
- - spec/ollama/utils/ansi_markdown_spec.rb
594
593
  - spec/ollama/utils/cache_fetcher_spec.rb
595
594
  - spec/ollama/utils/colorize_texts_spec.rb
596
595
  - spec/ollama/utils/fetcher_spec.rb
597
596
  - spec/ollama/utils/file_argument_spec.rb
598
597
  - spec/ollama/utils/tags_spec.rb
599
- - spec/ollama/utils/width_spec.rb
600
598
  - spec/spec_helper.rb
@@ -1,217 +0,0 @@
1
- require 'kramdown'
2
- require 'kramdown-parser-gfm'
3
- require 'terminal-table'
4
-
5
- class Ollama::Utils::ANSIMarkdown < Kramdown::Converter::Base
6
- include Term::ANSIColor
7
- include Ollama::Utils::Width
8
-
9
- class ::Kramdown::Parser::Mygfm < ::Kramdown::Parser::GFM
10
- def initialize(source, options)
11
- options[:gfm_quirks] << :no_auto_typographic
12
- super
13
- @block_parsers -= %i[
14
- definition_list block_html block_math
15
- footnote_definition abbrev_definition
16
- ]
17
- @span_parsers -= %i[ footnote_marker inline_math ]
18
- end
19
- end
20
-
21
- def self.parse(source)
22
- @doc = Kramdown::Document.new(
23
- source, input: :mygfm, auto_ids: false, entity_output: :as_char
24
- ).to_ansi
25
- end
26
-
27
- def initialize(root, options)
28
- super
29
- end
30
-
31
- def convert(el, opts = {})
32
- send("convert_#{el.type}", el, opts)
33
- end
34
-
35
- def inner(el, opts, &block)
36
- result = +''
37
- options = opts.dup.merge(parent: el)
38
- el.children.each_with_index do |inner_el, index|
39
- options[:index] = index
40
- options[:result] = result
41
- begin
42
- content = send("convert_#{inner_el.type}", inner_el, options)
43
- result << (block&.(inner_el, index, content) || content)
44
- rescue NameError => e
45
- warning "Caught #{e.class} for #{inner_el.type}"
46
- end
47
- end
48
- result
49
- end
50
-
51
- def convert_root(el, opts)
52
- inner(el, opts)
53
- end
54
-
55
- def convert_blank(_el, opts)
56
- opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n"
57
- end
58
-
59
- def convert_text(el, _opts)
60
- el.value
61
- end
62
-
63
- def convert_header(el, opts)
64
- newline bold { underline { inner(el, opts) } }
65
- end
66
-
67
- def convert_p(el, opts)
68
- length = width(percentage: 90) - opts[:list_indent].to_i
69
- length < 0 and return ''
70
- newline wrap(inner(el, opts), length:)
71
- end
72
-
73
- def convert_strong(el, opts)
74
- bold { inner(el, opts) }
75
- end
76
-
77
- def convert_em(el, opts)
78
- italic { inner(el, opts) }
79
- end
80
-
81
- def convert_a(el, opts)
82
- url = el.attr['href']
83
- hyperlink(url) { inner(el, opts) }
84
- end
85
-
86
- def convert_codespan(el, _opts)
87
- blue { el.value }
88
- end
89
-
90
- def convert_codeblock(el, _opts)
91
- blue { el.value }
92
- end
93
-
94
- def convert_blockquote(el, opts)
95
- newline ?“ + inner(el, opts).sub(/\n+\z/, '') + ?”
96
- end
97
-
98
- def convert_hr(_el, _opts)
99
- newline ?─ * width(percentage: 100)
100
- end
101
-
102
- def convert_img(el, _opts)
103
- url = el.attr['src']
104
- alt = el.attr['alt']
105
- alt.strip.size == 0 and alt = url
106
- alt = '🖼 ' + alt
107
- hyperlink(url) { alt }
108
- end
109
-
110
- def convert_ul(el, opts)
111
- list_indent = opts[:list_indent].to_i
112
- inner(el, opts) { |_inner_el, index, content|
113
- result = '· %s' % content
114
- result = newline(result, count: index <= el.children.size - 1 ? 1 : 2)
115
- result.gsub(/^/, ' ' * list_indent)
116
- }
117
- end
118
-
119
- def convert_ol(el, opts)
120
- list_indent = opts[:list_indent].to_i
121
- inner(el, opts) { |_inner_el, index, content|
122
- result = '%u. %s' % [ index + 1, content ]
123
- result = newline(result, count: index <= el.children.size - 1 ? 1 : 2)
124
- result.gsub(/^/, ' ' * list_indent)
125
- }
126
- end
127
-
128
- def convert_li(el, opts)
129
- opts = opts.dup
130
- opts[:list_indent] = 2 + opts[:list_indent].to_i
131
- newline inner(el, opts).sub(/\n+\Z/, '')
132
- end
133
-
134
- def convert_html_element(el, opts)
135
- if el.value == 'i' || el.value == 'em'
136
- italic { inner(el, opts) }
137
- elsif el.value == 'b' || el.value == 'strong'
138
- bold { inner(el, opts) }
139
- else
140
- ''
141
- end
142
- end
143
-
144
- def convert_table(el, opts)
145
- table = Terminal::Table.new
146
- table.style = {
147
- all_separators: true,
148
- border: :unicode_round,
149
- }
150
- opts[:table] = table
151
- inner(el, opts)
152
- el.options[:alignment].each_with_index do |a, i|
153
- a == :default and next
154
- opts[:table].align_column(i, a)
155
- end
156
- newline table.to_s
157
- end
158
-
159
- def convert_thead(el, opts)
160
- rows = inner(el, opts)
161
- rows = rows.split(/\s*\|\s*/)[1..].map(&:strip)
162
- opts[:table].headings = rows
163
- ''
164
- end
165
-
166
- def convert_tbody(el, opts)
167
- res = +''
168
- res << inner(el, opts)
169
- end
170
-
171
- def convert_tfoot(el, opts)
172
- ''
173
- end
174
-
175
- def convert_tr(el, opts)
176
- return '' if el.children.empty?
177
- full_width = width(percentage: 90)
178
- cols = el.children.map { |c| convert(c, opts).strip }
179
- row_size = cols.sum(&:size)
180
- return '' if row_size.zero?
181
- opts[:table] << cols.map { |c|
182
- length = (full_width * (c.size / row_size.to_f)).floor
183
- wrap(c, length:)
184
- }
185
- ''
186
- end
187
-
188
- def convert_td(el, opts)
189
- inner(el, opts)
190
- end
191
-
192
- def convert_entity(el, _opts)
193
- el.value.char
194
- end
195
-
196
- def convert_xml_comment(*)
197
- ''
198
- end
199
-
200
- def convert_xml_pi(*)
201
- ''
202
- end
203
-
204
- def convert_br(_el, opts)
205
- ''
206
- end
207
-
208
- def convert_smart_quote(el, _opts)
209
- el.value.to_s =~ /[rl]dquo/ ? "\"" : "'"
210
- end
211
-
212
- def newline(text, count: 1)
213
- text.gsub(/\n*\z/, ?\n * count)
214
- end
215
- end
216
-
217
- Kramdown::Converter.const_set(:Ansi, Ollama::Utils::ANSIMarkdown)
@@ -1,39 +0,0 @@
1
- require 'tins/terminal'
2
-
3
- module Ollama::Utils::Width
4
- include Term::ANSIColor
5
- extend Term::ANSIColor
6
-
7
- module_function
8
-
9
- def width(percentage: 100.0)
10
- ((Float(percentage) * Tins::Terminal.columns) / 100).floor
11
- end
12
-
13
- def wrap(text, percentage: nil, length: nil)
14
- percentage.nil? ^ length.nil? or
15
- raise ArgumentError, "either pass percentage or length argument"
16
- percentage and length ||= width(percentage:)
17
- text.gsub(/(?<!\n)\n(?!\n)/, ' ').lines.map do |line|
18
- if length >= 1 && uncolor { line }.length > length
19
- line.gsub(/(.{1,#{length}})(\s+|$)/, "\\1\n").strip
20
- else
21
- line.strip
22
- end
23
- end * ?\n
24
- end
25
-
26
- def truncate(text, percentage: nil, length: nil, ellipsis: ?…)
27
- percentage.nil? ^ length.nil? or
28
- raise ArgumentError, "either pass percentage or length argument"
29
- percentage and length ||= width(percentage:)
30
- ellipsis_length = ellipsis.size
31
- if length < ellipsis_length
32
- +''
33
- elsif text.size >= length + ellipsis_length
34
- text[0, length - ellipsis_length] + ellipsis
35
- else
36
- text
37
- end
38
- end
39
- end
@@ -1,15 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::ANSIMarkdown do
4
- let :source do
5
- File.read(Pathname.new(__dir__) + '..' + '..' + '..' + 'README.md')
6
- end
7
-
8
- it 'can parse' do
9
- File.open('tmp/README.ansi', ?w) do |output|
10
- ansi = described_class.parse(source)
11
- expect(ansi).to match("This is the end.")
12
- output.puts ansi
13
- end
14
- end
15
- end
@@ -1,82 +0,0 @@
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