ollama-ruby 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f80ae8ee6e8acbbedfff8b56923b25b583fc60b96c733985e32908874d542bb
4
- data.tar.gz: 80834beb676929f08f4216e373e56cadd12d16c0b50e95b1a599bdd48bf29c86
3
+ metadata.gz: c0e3428486bf4d60d1e4a64574a9c04bb41da5a5fe76b5f4bc37c5f089221cf3
4
+ data.tar.gz: 0a2f6f5269c97b22493400448204116aebef642273c024aee474a38741dfdce5
5
5
  SHA512:
6
- metadata.gz: 94823ec618f940056bcee6ac7d706c633087404efcb49122cdccc89a4596b4a5f14a0d1c1d60fd7bb13a28e10f3ba4dc2027cd96cfe384169b25acaf75f532b7
7
- data.tar.gz: ef8d4a7001c5502bc787f074f10dfaf16692b8fa90c4a6e32ae3c0c75b94da45b4691094229106cec72438837eef57b933cd5e5c9b28e63d0d4a598c152a9c32
6
+ metadata.gz: 3fd8db76a1cad30ba41af287e1abb77a102913f09f1648c9fbaf89f5e3bf4f3dace4a39cc6b72da456b633eb42d331b356134aecc196b112c7f94215177b4023
7
+ data.tar.gz: 4541a1e93c2714b786cb21b5e401ffc48a08e05c39bfac860e8cdd275a447bbc7db0b6cf80fee5f459f86734320a90362418a50f4174d53ece4e1a9d2f1dc647
data/CHANGES.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # Changes
2
2
 
3
+ ## 2024-09-12 v0.3.1
4
+
5
+ * Update dependencies and date in gemspec files:
6
+ - Updated `complex_config` dependency to '~> 0.22'
7
+ * Refactor FollowChat#eval_stats to add bold eval rates
8
+ * Improve formatting in eval_stats using bold and color for better
9
+ readability.
10
+ * Update import_document and add_image methods to handle nil values
11
+ correctly.
12
+ * Update width method in utils/width.rb to use uncolor when checking line
13
+ length.
14
+ * Refactor eval stats output in FollowChat class
15
+ - Add indentation to eval stats output for better readability
16
+ * FollowChat evaluation stats refactored
17
+ - Removed hardcoded eval_stats hash and replaced with method call
18
+ `eval_stats(response)`
19
+ - Added new method `eval_stats(response)` to calculate evaluation statistics
20
+ - Calculates eval duration, prompt eval duration, total duration, and load
21
+ duration
22
+ - Adds eval count, prompt eval count, eval rate, and prompt eval rate
23
+ * Use default to_s tree representation of config.
24
+ * Update complex_config dependency to ~> 0.21, >= 0.21.1 in Rakefile
25
+ * Update complex_config dependency to ~> 0.21, >= 0.21.1 in
26
+ ollama-ruby.gemspec
27
+ * Update dependencies and configuration display
28
+ * Update 'complex_config' dependency to '~> 0.21'
29
+ * Change OllamaChatConfig to display configuration as a tree instead of yaml
30
+ * Improve /web search command
31
+ * Update infobar dependency to ~> 0.8
32
+ * Update /web command to summarize web sources as well as importing them
33
+ directly
34
+ * Add /clobber command to clear conversation messages and collection
35
+ * Refactor Ollama chat configuration and summary generation.
36
+ * Update `OllamaChatConfig` to use `prompts.system` instead of `system`.
37
+ * Introduce `prompts.summarize` config as template for generating abstract
38
+ summaries.
39
+ * Replace hardcoded summary generation with call to `prompts.summarize`.
40
+ * Display /help for all unknown chat commands starting wit `/`
41
+
42
+ ## 2024-09-05 v0.3.0
43
+
44
+ * **New Features**
45
+ * Created new file `ollama_cli` with Ollama CLI functionality.
46
+ * Added executable `ollama_cli` to s.executables in ollama-ruby.gemspec.
47
+ * Added `find_where` method in `documents.rb` to filter records by text size
48
+ and count.
49
+ * Added test for `find_where` method in `documents_spec.rb`.
50
+ * Features for `ollama_chat`
51
+ * Added `found_texts_count` option to `OllamaChatConfig`.
52
+ * Implemented `parse_rss` method for RSS feeds and `parse_atom` method
53
+ for Atom feeds.
54
+ * Added links to titles in RSS feed item summaries and Atom feed item
55
+ summaries.
56
+ * Updated `parse_source` method to handle different content types,
57
+ including HTML, XML, and RSS/Atom feeds.
58
+ * Added `/web [n] query` command to search web and return n or 1 results
59
+ in chat interface.
60
+ * **Improvements**
61
+ * Improved validation for system prompts
62
+ * Extracted file argument handling into a separate module and method
63
+ * Added default value for config or model system prompt
64
+ * Improved input validation for `system_prompt` path
65
+ * Updated collection clearing logic to accept optional tags parameter
66
+ * Updated `Tags` class to overload `to_a` method for converting to array of
67
+ strings
68
+
3
69
  ## 2024-09-03 v0.2.0
4
70
 
5
71
  ### Changes
data/README.md CHANGED
@@ -152,19 +152,21 @@ subject - the young, blue-eyed cat.
152
152
  The following commands can be given inside the chat, if prefixed by a `/`:
153
153
 
154
154
  ```
155
- /paste to paste content
156
- /markdown toggle markdown output
157
- /list list the messages of the conversation
158
- /clear clear the conversation messages
159
- /pop [n] pop the last n exchanges, defaults to 1
160
- /model change the model
161
- /regenerate the last answer message
162
- /collection clear|stats|change|new clear or show stats of current collection
163
- /summarize source summarize the URL/file source's content
164
- /save filename store conversation messages
165
- /load filename load conversation messages
166
- /quit to quit
167
- /help to view this help
155
+ /paste to paste content
156
+ /markdown toggle markdown output
157
+ /list list the messages of the conversation
158
+ /clear clear the conversation messages
159
+ /clobber clear conversation messages and collection
160
+ /pop [n] pop the last n exchanges, defaults to 1
161
+ /model change the model
162
+ /regenerate the last answer message
163
+ /collection clear [tag]|stats|change|new clear or show stats of current collection
164
+ /summarize source summarize the URL/file source's content
165
+ /web [n] query query web search & return n or 1 results
166
+ /save filename store conversation messages
167
+ /load filename load conversation messages
168
+ /quit to quit
169
+ /help to view this help
168
170
  ```
169
171
 
170
172
  ### ollama\_console
data/Rakefile CHANGED
@@ -18,12 +18,13 @@ GemHadar do
18
18
  '.utilsrc', '.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
19
19
  readme 'README.md'
20
20
 
21
- executables << 'ollama_console' << 'ollama_chat' << 'ollama_update'
21
+ executables << 'ollama_console' << 'ollama_chat' <<
22
+ 'ollama_update' << 'ollama_cli'
22
23
 
23
24
  required_ruby_version '~> 3.1'
24
25
 
25
26
  dependency 'excon', '~> 0.111'
26
- dependency 'infobar', '~> 0.7'
27
+ dependency 'infobar', '~> 0.8'
27
28
  dependency 'term-ansicolor', '~> 1.11'
28
29
  dependency 'kramdown-parser-gfm', '~> 1.1'
29
30
  dependency 'terminal-table', '~> 3.0'
@@ -33,9 +34,10 @@ GemHadar do
33
34
  dependency 'sorted_set', '~> 1.0'
34
35
  dependency 'mime-types', '~> 3.0'
35
36
  dependency 'reverse_markdown', '~> 2.0'
36
- dependency 'complex_config', '~> 0.20'
37
+ dependency 'complex_config', '~> 0.22'
37
38
  dependency 'search_ui', '~> 0.0'
38
39
  dependency 'amatch', '~> 0.4.1'
40
+ dependency 'pdf-reader', '~> 2.0'
39
41
  development_dependency 'all_images', '~> 0.4'
40
42
  development_dependency 'rspec', '~> 3.2'
41
43
  development_dependency 'utils'
data/bin/ollama_chat CHANGED
@@ -12,6 +12,8 @@ require 'complex_config'
12
12
  require 'fileutils'
13
13
  require 'uri'
14
14
  require 'nokogiri'
15
+ require 'rss'
16
+ require 'pdf/reader'
15
17
 
16
18
  class OllamaChatConfig
17
19
  include ComplexConfig
@@ -24,7 +26,12 @@ class OllamaChatConfig
24
26
  name: <%= ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1') %>
25
27
  options:
26
28
  num_ctx: 8192
27
- system: <%= ENV.fetch('OLLAMA_CHAT_SYSTEM', 'null') %>
29
+ prompts:
30
+ system: <%= ENV.fetch('OLLAMA_CHAT_SYSTEM', 'null') %>
31
+ summarize: |
32
+ Generate an abstract summary of the content in this document:
33
+
34
+ %s
28
35
  voice: Samantha
29
36
  markdown: true
30
37
  embedding:
@@ -36,6 +43,7 @@ class OllamaChatConfig
36
43
  prompt: 'Represent this sentence for searching relevant passages: %s'
37
44
  collection: <%= ENV.fetch('OLLAMA_CHAT_COLLECTION', 'ollama_chat') %>
38
45
  found_texts_size: 4096
46
+ found_texts_count: null
39
47
  splitter:
40
48
  name: RecursiveCharacter
41
49
  chunk_size: 1024
@@ -47,7 +55,7 @@ class OllamaChatConfig
47
55
 
48
56
  def initialize(filename = nil)
49
57
  @filename = filename || default_path
50
- @config = Provider.config(@filename)
58
+ @config = Provider.config(@filename, '⚙️')
51
59
  retried = false
52
60
  rescue ConfigurationFileMissing
53
61
  if @filename == default_path && !retried
@@ -112,19 +120,28 @@ class FollowChat
112
120
  @say.call(response)
113
121
  end
114
122
  if response.done
115
- @output.puts
116
- eval_stats = {
117
- eval_duration: Tins::Duration.new(response.eval_duration / 1e9),
118
- eval_count: response.eval_count,
119
- prompt_eval_duration: Tins::Duration.new(response.prompt_eval_duration / 1e9),
120
- prompt_eval_count: response.prompt_eval_count,
121
- total_duration: Tins::Duration.new(response.total_duration / 1e9),
122
- load_duration: Tins::Duration.new(response.load_duration / 1e9),
123
- }.map { _1 * '=' } * ' '
124
- @output.puts '📊 ' + color(111) { Utils::Width.wrap(eval_stats, percentage: 90) }
123
+ @output.puts "", eval_stats(response)
125
124
  end
126
125
  self
127
126
  end
127
+
128
+ def eval_stats(response)
129
+ eval_duration = response.eval_duration / 1e9
130
+ prompt_eval_duration = response.prompt_eval_duration / 1e9
131
+ stats_text = {
132
+ eval_duration: Tins::Duration.new(eval_duration),
133
+ eval_count: response.eval_count,
134
+ eval_rate: bold { "%.2f c/s" % (response.eval_count / eval_duration) } + color(111),
135
+ prompt_eval_duration: Tins::Duration.new(prompt_eval_duration),
136
+ prompt_eval_count: response.prompt_eval_count,
137
+ prompt_eval_rate: bold { "%.2f c/s" % (response.prompt_eval_count / prompt_eval_duration) } + color(111),
138
+ total_duration: Tins::Duration.new(response.total_duration / 1e9),
139
+ load_duration: Tins::Duration.new(response.load_duration / 1e9),
140
+ }.map { _1 * '=' } * ' '
141
+ '📊 ' + color(111) {
142
+ Utils::Width.wrap(stats_text, percentage: 90).gsub(/(?<!\A)^/, ' ')
143
+ }
144
+ end
128
145
  end
129
146
 
130
147
  def search_web(query, n = 5)
@@ -153,7 +170,8 @@ end
153
170
 
154
171
  def pull_model_unless_present(model, options, retried = false)
155
172
  ollama.show(name: model) { |response|
156
- puts "Model #{bold{model}} with architecture #{response.model_info['general.architecture']} found."
173
+ puts "Model #{bold{model}} with architecture "\
174
+ "#{response.model_info['general.architecture']} found."
157
175
  if system = response.system
158
176
  puts "Configured model system prompt is:\n#{italic { system }}"
159
177
  return system
@@ -226,17 +244,77 @@ def list_conversation(messages, markdown)
226
244
  end
227
245
  end
228
246
 
247
+ def reverse_markdown(html)
248
+ ReverseMarkdown.convert(
249
+ html,
250
+ unknown_tags: :bypass,
251
+ github_flavored: true,
252
+ tag_border: ''
253
+ )
254
+ end
255
+
256
+ def parse_rss(source_io)
257
+ feed = RSS::Parser.parse(source_io, false, false)
258
+ title = <<~end
259
+ # #{feed&.channel&.title}
260
+
261
+ end
262
+ feed.items.inject(title) do |text, item|
263
+ text << <<~end
264
+ ## [#{item&.title}](#{item&.link})
265
+
266
+ updated on #{item&.pubDate}
267
+
268
+ #{reverse_markdown(item&.description)}
269
+
270
+ end
271
+ end
272
+ end
273
+
274
+ def parse_atom(source_io)
275
+ feed = RSS::Parser.parse(source_io, false, false)
276
+ title = <<~end
277
+ # #{feed.title.content}
278
+
279
+ end
280
+ feed.items.inject(title) do |text, item|
281
+ text << <<~end
282
+ ## [#{item&.title&.content}](#{item&.link&.href})
283
+
284
+ updated on #{item&.updated&.content}
285
+
286
+ #{reverse_markdown(item&.content&.content)}
287
+
288
+ end
289
+ end
290
+ end
291
+
229
292
  def parse_source(source_io)
230
- case source_io&.content_type&.sub_type
231
- when 'html'
232
- ReverseMarkdown.convert(
233
- source_io.read,
234
- unknown_tags: :bypass,
235
- github_flavored: true,
236
- tag_border: ''
237
- )
238
- when 'plain', 'csv', 'xml'
293
+ case source_io&.content_type
294
+ when 'text/html'
295
+ reverse_markdown(source_io.read)
296
+ when 'text/xml'
297
+ if source_io.readline =~ %r(^\s*<rss\s)
298
+ source_io.rewind
299
+ return parse_rss(source_io)
300
+ end
301
+ source_io.rewind
302
+ source_io.read
303
+ when %r(\Atext/)
239
304
  source_io.read
305
+ when 'application/rss+xml'
306
+ parse_rss(source_io)
307
+ when 'application/atom+xml'
308
+ parse_atom(source_io)
309
+ when 'application/json'
310
+ source_io.read
311
+ when 'application/pdf'
312
+ reader = PDF::Reader.new(source_io)
313
+ result = +''
314
+ reader.pages.each do |page|
315
+ result << page.text
316
+ end
317
+ result
240
318
  else
241
319
  STDERR.puts "Cannot import #{source_io&.content_type} document."
242
320
  return
@@ -248,7 +326,7 @@ def import_document(source_io, source)
248
326
  STDOUT.puts "Embedding disabled, I won't import any documents, try: /summarize"
249
327
  return
250
328
  end
251
- infobar.puts "Importing #{italic { source_io.content_type }} document #{source.to_s.inspect}."
329
+ puts "Importing #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
252
330
  text = parse_source(source_io) or return
253
331
  text.downcase!
254
332
  splitter_config = $config.embedding.splitter
@@ -276,7 +354,7 @@ def import_document(source_io, source)
276
354
  end
277
355
 
278
356
  def add_image(images, source_io, source)
279
- STDERR.puts "Adding #{source_io.content_type} image #{source.to_s.inspect}."
357
+ STDERR.puts "Adding #{source_io&.content_type} image #{source.to_s.inspect}."
280
358
  image = Image.for_io(source_io, path: source.to_s)
281
359
  (images << image).uniq!
282
360
  end
@@ -301,16 +379,12 @@ rescue => e
301
379
  end
302
380
 
303
381
  def summarize(source)
304
- puts "Now summarizing #{source.inspect}."
382
+ puts "Now summarizing #{source.to_s.inspect}."
305
383
  source_content =
306
384
  fetch_source(source) do |source_io|
307
385
  parse_source(source_io) or return
308
386
  end
309
- <<~end
310
- # Generate an abstract summary of the content in this document:
311
-
312
- #{source_content}
313
- end
387
+ $config.prompts.summarize % source_content
314
388
  end
315
389
 
316
390
  def parse_content(content, images)
@@ -327,7 +401,7 @@ def parse_content(content, images)
327
401
  case source_io&.content_type&.media_type
328
402
  when 'image'
329
403
  add_image(images, source_io, source)
330
- when 'text'
404
+ when 'text', 'application'
331
405
  import_document(source_io, source)
332
406
  else
333
407
  STDERR.puts(
@@ -391,19 +465,21 @@ end
391
465
 
392
466
  def display_chat_help
393
467
  puts <<~end
394
- /paste to paste content
395
- /markdown toggle markdown output
396
- /list list the messages of the conversation
397
- /clear clear the conversation messages
398
- /pop [n] pop the last n exchanges, defaults to 1
399
- /model change the model
400
- /regenerate the last answer message
401
- /collection clear|stats|change|new clear or show stats of current collection
402
- /summarize source summarize the URL/file source's content
403
- /save filename store conversation messages
404
- /load filename load conversation messages
405
- /quit to quit
406
- /help to view this help
468
+ /paste to paste content
469
+ /markdown toggle markdown output
470
+ /list list the messages of the conversation
471
+ /clear clear the conversation messages
472
+ /clobber clear conversation messages and collection
473
+ /pop [n] pop the last n exchanges, defaults to 1
474
+ /model change the model
475
+ /regenerate the last answer message
476
+ /collection clear [tag]|stats|change|new clear or show stats of current collection
477
+ /summarize source summarize the URL/file source's content
478
+ /web [n] query query web search & return n or 1 results
479
+ /save filename store conversation messages
480
+ /load filename load conversation messages
481
+ /quit to quit
482
+ /help to view this help
407
483
  end
408
484
  end
409
485
 
@@ -436,8 +512,7 @@ $config = config.config
436
512
 
437
513
  opts[?h] and usage
438
514
 
439
- puts "Configuration read from #{config.filename.inspect} is:"
440
- y $config.to_h
515
+ puts "Configuration read from #{config.filename.inspect} is:", $config
441
516
 
442
517
  base_url = opts[?u] || $config.url
443
518
  $ollama = Client.new(base_url:, debug: $config.debug)
@@ -475,7 +550,7 @@ if $config.embedding.enabled
475
550
  File.expand_path(doc)
476
551
  end
477
552
  end
478
- infobar.puts "Collection #{bold{collection}}: Adding #{document_list.size} documents…"
553
+ puts "Collection #{bold{collection}}: Adding #{document_list.size} documents…"
479
554
  document_list.each_slice(25) do |docs|
480
555
  docs.each do |doc|
481
556
  fetch_source(doc) do |doc_io|
@@ -497,23 +572,16 @@ markdown = set_markdown($config.markdown)
497
572
  if opts[?c]
498
573
  messages.concat load_conversation(opts[?c])
499
574
  else
500
- system = nil
501
- if system_prompt_file = opts[?s]
502
- system = File.read(system_prompt_file)
503
- end
504
- system ||= $config.system
505
-
506
- if system
575
+ if system = Ollama::Utils::FileArgument.
576
+ get_file_argument(opts[?s], default: $config.prompts.system? || model_system)
507
577
  messages << Message.new(role: 'system', content: system)
508
578
  puts "Configured system prompt is:\n#{italic { system }}"
509
- elsif model_system.present?
510
- puts "Using model system prompt."
511
579
  end
512
580
  end
513
581
 
514
582
  puts "\nType /help to display the chat help."
515
583
 
516
- images = []
584
+ images = []
517
585
  loop do
518
586
  parse_content = true
519
587
  input_prompt = bold { color(172) { message_type(images) + " user" } } + bold { "> " }
@@ -526,7 +594,7 @@ loop do
526
594
  when %r(^/quit$)
527
595
  puts "Goodbye."
528
596
  exit 0
529
- when %r(^/markdown)
597
+ when %r(^/markdown$)
530
598
  markdown = set_markdown(!markdown)
531
599
  next
532
600
  when %r(^/list$)
@@ -536,11 +604,23 @@ loop do
536
604
  messages.clear
537
605
  puts "Cleared messages."
538
606
  next
539
- when %r(^/collection (clear|stats|change|new)$)
540
- case $1
607
+ when %r(^/clobber$)
608
+ messages.clear
609
+ $documents.clear
610
+ puts "Cleared messages and collection."
611
+ next
612
+ when %r(^/collection\s+(clear|stats|change|new)(?:\s+(.+))?$)
613
+ command, arg = $1, $2
614
+ case command
541
615
  when 'clear'
542
- $documents.clear
543
- puts "Cleared collection #{bold{collection}}."
616
+ tags = arg.present? ? arg.sub(/\A#*/, '') : nil
617
+ if tags
618
+ $documents.clear(tags:)
619
+ puts "Cleared tag ##{tags} from collection #{bold{collection}}."
620
+ else
621
+ $documents.clear
622
+ puts "Cleared collection #{bold{collection}}."
623
+ end
544
624
  when 'stats'
545
625
  collection_stats
546
626
  when 'change'
@@ -571,13 +651,19 @@ loop do
571
651
  when %r(^/summarize\s+(.+))
572
652
  parse_content = false
573
653
  content = summarize($1) or next
574
- when %r(^/web\s+(?:(\d+)\s+)(.+)$)
575
- parse_content = true
576
- urls = search_web($2, $1.to_i)
654
+ when %r(^/web\s+(?:(\d+)\s+)?(.+))
655
+ parse_content = false
656
+ urls = search_web($2, $1.to_i)
657
+ urls.each do |url|
658
+ fetch_source(url) do |url_io|
659
+ import_document(url_io, url)
660
+ end
661
+ end
662
+ urls_summarized = urls.map { summarize(_1) }
577
663
  content = <<~end
578
- Answer the the query #{$2.inspect} using these sources:
664
+ Answer the the query #{$2.inspect} using these sources and summaries:
579
665
 
580
- #{urls * ?\n}
666
+ #{urls.zip(urls_summarized).map { |u, s| "%s as \n:%s" % [ u, s ] } * "\n\n"}
581
667
  end
582
668
  when %r(^/save\s+(.+)$)
583
669
  save_conversation($1, messages)
@@ -587,7 +673,7 @@ loop do
587
673
  messages = load_conversation($1)
588
674
  puts "Loaded conversation from #$1."
589
675
  next
590
- when %r(^/help$)
676
+ when %r(^/)
591
677
  display_chat_help
592
678
  next
593
679
  when nil, ''
@@ -602,15 +688,13 @@ loop do
602
688
  end
603
689
 
604
690
  if $config.embedding.enabled && content
605
- records = $documents.find(
691
+ records = $documents.find_where(
606
692
  content.downcase,
607
693
  tags:,
608
- prompt: $config.embedding.model.prompt?
694
+ prompt: $config.embedding.model.prompt?,
695
+ text_size: $config.embedding.found_texts_size?,
696
+ text_count: $config.embedding.found_texts_count?,
609
697
  )
610
- s, found_texts_size = 0, $config.embedding.found_texts_size
611
- records = records.take_while {
612
- (s += _1.text.size) <= found_texts_size
613
- }
614
698
  found_texts = records.map(&:text)
615
699
  unless found_texts.empty?
616
700
  content += "\nConsider these chunks for your answer:\n"\
data/bin/ollama_cli ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ollama'
4
+ include Ollama
5
+ include Ollama::Utils::FileArgument
6
+ require 'tins'
7
+ include Tins::GO
8
+ require 'json'
9
+
10
+ def usage
11
+ puts <<~end
12
+ #{File.basename($0)} [OPTIONS]
13
+
14
+ -u URL the ollama base url, OLLAMA_URL
15
+ -m MODEL the ollama model to chat with, OLLAMA_MODEL
16
+ -M OPTIONS the ollama model options to use, OLLAMA_MODEL_OPTIONS
17
+ -s SYSTEM the system prompt to use as a file, OLLAMA_SYSTEM
18
+ -p PROMPT the user prompt to use as a file, OLLAMA_PROMPT
19
+ -H HANDLER the handler to use for the response, defaults to Print
20
+ -S use streaming for generation
21
+ -h this help
22
+
23
+ end
24
+ exit 0
25
+ end
26
+
27
+ opts = go 'u:m:M:s:p:H:Sh', defaults: { ?H => 'Print', ?M => '{}' }
28
+
29
+ opts[?h] and usage
30
+
31
+ base_url = opts[?u] || ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
32
+ model = opts[?m] || ENV.fetch('OLLAMA_MODEL', 'llama3.1')
33
+ options = Ollama::Options.from_hash(JSON(
34
+ get_file_argument(opts[?M], default: ENV['OLLAMA_MODEL_OPTIONS'])
35
+ ))
36
+ system = get_file_argument(opts[?s], default: ENV['OLLAMA_SYSTEM'])
37
+ prompt = get_file_argument(opts[?p], default: ENV['OLLAMA_PROMPT'])
38
+
39
+ if prompt.nil?
40
+ prompt = STDIN.read
41
+ elsif c = prompt.scan('%s').size
42
+ case c
43
+ when 0
44
+ when 1
45
+ prompt = prompt % STDIN.read
46
+ else
47
+ STDERR.puts "Found more than one plaeceholder %s. => Ignoring."
48
+ end
49
+ end
50
+
51
+ if ENV['DEBUG'].to_i == 1
52
+ puts <<~EOT
53
+ base_url = #{base_url.inspect}
54
+ model = #{model.inspect}
55
+ system = #{system.inspect}
56
+ prompt = #{prompt.inspect}
57
+ options = #{options.to_json}
58
+ EOT
59
+ end
60
+
61
+ Client.new(base_url:, read_timeout: 120).generate(
62
+ model:,
63
+ system:,
64
+ prompt:,
65
+ options:,
66
+ stream: !!opts[?S],
67
+ &Object.const_get(opts[?H])
68
+ )
data/bin/ollama_console CHANGED
@@ -5,8 +5,13 @@ include Ollama
5
5
  require 'irb'
6
6
  require 'irb/history'
7
7
 
8
- base_url = ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
9
- ollama = Client.new(base_url:)
8
+ def base_url
9
+ ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
10
+ end
11
+
12
+ def ollama
13
+ $ollama ||= Client.new(base_url:)
14
+ end
10
15
  IRB.setup nil
11
16
  IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
12
17
  IRB.conf[:HISTORY_FILE] = File.join(ENV.fetch('HOME'), '.ollama_console-history')
@@ -36,9 +36,11 @@ class Ollama::Documents::MemoryCache
36
36
  end
37
37
  include Enumerable
38
38
 
39
- private
40
-
41
39
  def pre(key)
42
40
  [ @prefix, key ].join
43
41
  end
42
+
43
+ def unpre(key)
44
+ key.sub(/\A#@prefix/, '')
45
+ end
44
46
  end
@@ -13,7 +13,10 @@ class Ollama::Documents::RedisCache
13
13
  end
14
14
 
15
15
  def [](key)
16
- JSON(redis.get(pre(key)), object_class: Ollama::Documents::Record)
16
+ value = redis.get(pre(key))
17
+ unless value.nil?
18
+ JSON(value, object_class: Ollama::Documents::Record)
19
+ end
17
20
  end
18
21
 
19
22
  def []=(key, value)
@@ -45,8 +48,6 @@ class Ollama::Documents::RedisCache
45
48
  end
46
49
  include Enumerable
47
50
 
48
- private
49
-
50
51
  def pre(key)
51
52
  [ @prefix, key ].join
52
53
  end
@@ -12,6 +12,7 @@ require 'ollama/documents/splitters/semantic'
12
12
 
13
13
  class Ollama::Documents
14
14
  include Ollama::Utils::Math
15
+ include Ollama::Utils::Width
15
16
 
16
17
  class Record < JSON::GenericObject
17
18
  def to_s
@@ -42,7 +43,7 @@ class Ollama::Documents
42
43
  def add(inputs, batch_size: 10, source: nil, tags: [])
43
44
  inputs = Array(inputs)
44
45
  tags = Ollama::Utils::Tags.new(tags)
45
- source and tags.add File.basename(source)
46
+ source and tags.add File.basename(source).gsub(/\?.*/, '')
46
47
  inputs.map! { |i|
47
48
  text = i.respond_to?(:read) ? i.read : i.to_s
48
49
  text
@@ -51,7 +52,7 @@ class Ollama::Documents
51
52
  inputs.empty? and return self
52
53
  batches = inputs.each_slice(batch_size).
53
54
  with_infobar(
54
- label: "Add #{tags}",
55
+ label: "Add #{truncate(tags.to_s, percentage: 25)}",
55
56
  total: inputs.size
56
57
  )
57
58
  batches.each do |batch|
@@ -87,8 +88,18 @@ class Ollama::Documents
87
88
  @cache.size
88
89
  end
89
90
 
90
- def clear
91
- @cache.clear
91
+ def clear(tags: nil)
92
+ if tags
93
+ tags = Ollama::Utils::Tags.new(Array(tags)).to_a
94
+ @cache.each do |key, record|
95
+ if (tags & record.tags).size >= 1
96
+ @cache.delete(@cache.unpre(key))
97
+ end
98
+ end
99
+ else
100
+ @cache.clear
101
+ end
102
+ self
92
103
  end
93
104
 
94
105
  def find(string, tags: nil, prompt: nil)
@@ -96,7 +107,7 @@ class Ollama::Documents
96
107
  needle_norm = norm(needle)
97
108
  records = @cache
98
109
  if tags
99
- tags = Ollama::Utils::Tags.new(tags)
110
+ tags = Ollama::Utils::Tags.new(tags).to_a
100
111
  records = records.select { |_key, record| (tags & record.tags).size >= 1 }
101
112
  end
102
113
  records = records.sort_by { |key, record|
@@ -111,6 +122,20 @@ class Ollama::Documents
111
122
  records.transpose.last&.reverse.to_a
112
123
  end
113
124
 
125
+ def find_where(string, text_size: nil, text_count: nil, **opts)
126
+ records = find(string, **opts)
127
+ size, count = 0, 0
128
+ records.take_while do |record|
129
+ if text_size and (size += record.text.size) > text_size
130
+ next false
131
+ end
132
+ if text_count and (count += 1) > text_count
133
+ next false
134
+ end
135
+ true
136
+ end
137
+ end
138
+
114
139
  def collections
115
140
  case @cache
116
141
  when MemoryCache
@@ -0,0 +1,16 @@
1
+ module Ollama::Utils::FileArgument
2
+ module_function
3
+
4
+ def get_file_argument(prompt, default: nil)
5
+ if prompt.present? && prompt.size < 2 ** 15 &&
6
+ File.basename(prompt).size < 2 ** 8 &&
7
+ File.exist?(prompt)
8
+ then
9
+ File.read(prompt)
10
+ elsif prompt.present?
11
+ prompt
12
+ else
13
+ default
14
+ end
15
+ end
16
+ end
@@ -1,6 +1,10 @@
1
1
  require 'sorted_set'
2
2
 
3
3
  class Ollama::Utils::Tags < SortedSet
4
+ def to_a
5
+ super.map(&:to_s)
6
+ end
7
+
4
8
  def to_s
5
9
  map { |t| '#%s' % t } * ' '
6
10
  end
@@ -1,6 +1,8 @@
1
1
  require 'tins/terminal'
2
2
 
3
3
  module Ollama::Utils::Width
4
+ include Term::ANSIColor
5
+
4
6
  module_function
5
7
 
6
8
  def width(percentage: 100.0)
@@ -12,11 +14,24 @@ module Ollama::Utils::Width
12
14
  raise ArgumentError, "either pass percentage or length argument"
13
15
  percentage and length ||= width(percentage:)
14
16
  text.gsub(/(?<!\n)\n(?!\n)/, ' ').lines.map do |line|
15
- if length >= 1 && line.length > length
17
+ if length >= 1 && uncolor { line }.length > length
16
18
  line.gsub(/(.{1,#{length}})(\s+|$)/, "\\1\n").strip
17
19
  else
18
20
  line.strip
19
21
  end
20
22
  end * ?\n
21
23
  end
24
+
25
+ def truncate(text, percentage: nil, length: nil, ellipsis: ?…)
26
+ percentage.nil? ^ length.nil? or
27
+ raise ArgumentError, "either pass percentage or length argument"
28
+ percentage and length ||= width(percentage:)
29
+ if length < 1
30
+ +''
31
+ elsif text.size > length
32
+ text[0, length - 1] + ?…
33
+ else
34
+ text
35
+ end
36
+ end
22
37
  end
@@ -1,6 +1,6 @@
1
1
  module Ollama
2
2
  # Ollama version
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.1'
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
@@ -18,6 +18,7 @@ require 'ollama/utils/math'
18
18
  require 'ollama/utils/colorize_texts'
19
19
  require 'ollama/utils/fetcher'
20
20
  require 'ollama/utils/chooser'
21
+ require 'ollama/utils/file_argument'
21
22
 
22
23
  require 'ollama/version'
23
24
  require 'ollama/errors'
data/ollama-ruby.gemspec CHANGED
@@ -1,36 +1,36 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama-ruby 0.2.0 ruby lib
2
+ # stub: ollama-ruby 0.3.1 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama-ruby".freeze
6
- s.version = "0.2.0".freeze
6
+ s.version = "0.3.1".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-09-03"
11
+ s.date = "2024-09-12"
12
12
  s.description = "Library that allows interacting with the Ollama API".freeze
13
13
  s.email = "flori@ping.de".freeze
14
- s.executables = ["ollama_console".freeze, "ollama_chat".freeze, "ollama_update".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/memory_cache.rb".freeze, "lib/ollama/documents/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/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.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_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/memory_cache.rb".freeze, "lib/ollama/documents/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/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.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/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_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/fetcher_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
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/memory_cache.rb".freeze, "lib/ollama/documents/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/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/memory_cache.rb".freeze, "lib/ollama/documents/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/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/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_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/fetcher_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
- s.rubygems_version = "3.5.11".freeze
21
+ s.rubygems_version = "3.5.18".freeze
22
22
  s.summary = "Interacting with the Ollama API".freeze
23
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_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/fetcher_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
 
27
- s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 1.17.0".freeze])
27
+ s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 1.17.1".freeze])
28
28
  s.add_development_dependency(%q<all_images>.freeze, ["~> 0.4".freeze])
29
29
  s.add_development_dependency(%q<rspec>.freeze, ["~> 3.2".freeze])
30
30
  s.add_development_dependency(%q<utils>.freeze, [">= 0".freeze])
31
31
  s.add_development_dependency(%q<webmock>.freeze, [">= 0".freeze])
32
32
  s.add_runtime_dependency(%q<excon>.freeze, ["~> 0.111".freeze])
33
- s.add_runtime_dependency(%q<infobar>.freeze, ["~> 0.7".freeze])
33
+ s.add_runtime_dependency(%q<infobar>.freeze, ["~> 0.8".freeze])
34
34
  s.add_runtime_dependency(%q<term-ansicolor>.freeze, ["~> 1.11".freeze])
35
35
  s.add_runtime_dependency(%q<kramdown-parser-gfm>.freeze, ["~> 1.1".freeze])
36
36
  s.add_runtime_dependency(%q<terminal-table>.freeze, ["~> 3.0".freeze])
@@ -40,7 +40,8 @@ Gem::Specification.new do |s|
40
40
  s.add_runtime_dependency(%q<sorted_set>.freeze, ["~> 1.0".freeze])
41
41
  s.add_runtime_dependency(%q<mime-types>.freeze, ["~> 3.0".freeze])
42
42
  s.add_runtime_dependency(%q<reverse_markdown>.freeze, ["~> 2.0".freeze])
43
- s.add_runtime_dependency(%q<complex_config>.freeze, ["~> 0.20".freeze])
43
+ s.add_runtime_dependency(%q<complex_config>.freeze, ["~> 0.22".freeze])
44
44
  s.add_runtime_dependency(%q<search_ui>.freeze, ["~> 0.0".freeze])
45
45
  s.add_runtime_dependency(%q<amatch>.freeze, ["~> 0.4.1".freeze])
46
+ s.add_runtime_dependency(%q<pdf-reader>.freeze, ["~> 2.0".freeze])
46
47
  end
@@ -74,5 +74,13 @@ RSpec.describe Ollama::Documents::RedisCache do
74
74
  expect(redis).to receive(:scan_each).with(match: 'test-*')
75
75
  redis_cache.to_a
76
76
  end
77
+
78
+ it 'can compute prefix with pre' do
79
+ expect(redis_cache.pre('foo')).to eq 'test-foo'
80
+ end
81
+
82
+ it 'can remove prefix with unpre' do
83
+ expect(redis_cache.unpre('test-foo')).to eq 'foo'
84
+ end
77
85
  end
78
86
  end
@@ -76,6 +76,34 @@ RSpec.describe Ollama::Documents do
76
76
  expect(records[0].to_s).to eq '#<Ollama::Documents::Record "foo" #test 1.0>'
77
77
  end
78
78
 
79
+ it 'can find strings conditionally' do
80
+ allow(ollama).to receive(:embed).
81
+ with(model:, input: [ 'foobar' ], options: nil).
82
+ and_return(double(embeddings: [ [ 0.01 ] ]))
83
+ allow(ollama).to receive(:embed).
84
+ with(model:, input: [ 'foo' ], options: nil).
85
+ and_return(double(embeddings: [ [ 0.1 ] ]))
86
+ expect(documents << 'foobar').to eq documents
87
+ expect(documents << 'foo').to eq documents
88
+ expect(ollama).to receive(:embed).at_least(:once).
89
+ with(model:, input: 'foo', options: nil).
90
+ and_return(double(embeddings: [ [ 0.1 ] ]))
91
+ records = documents.find_where('foo', text_count: 1)
92
+ expect(records).to eq [
93
+ Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
94
+ ]
95
+ records = documents.find_where('foo', text_size: 3)
96
+ expect(records).to eq [
97
+ Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
98
+ ]
99
+ records = documents.find_where('foo')
100
+ expect(records).to eq [
101
+ Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
102
+ Ollama::Documents::Record[text: 'foobar', embedding: [ 0.1 ], similarity: 1.0 ],
103
+ ]
104
+ end
105
+
106
+
79
107
  context 'it uses cache' do
80
108
  before do
81
109
  allow(ollama).to receive(:embed).
@@ -103,6 +131,20 @@ RSpec.describe Ollama::Documents do
103
131
  }.to change { documents.size }.from(1).to(0)
104
132
  end
105
133
 
134
+ it 'can clear texts with tags' do
135
+ allow(ollama).to receive(:embed).
136
+ with(model:, input: %w[ bar ], options: nil).
137
+ and_return(double(embeddings: [ [ 0.1 ] ]))
138
+ expect(documents.add('foo', tags: %i[ test ])).to eq documents
139
+ expect(documents.add('bar', tags: %i[ test2 ])).to eq documents
140
+ expect {
141
+ documents.clear tags: 'test'
142
+ }.to change { documents.size }.from(2).to(1)
143
+ expect {
144
+ documents.clear tags: :test2
145
+ }.to change { documents.size }.from(1).to(0)
146
+ end
147
+
106
148
  it 'returns collections' do
107
149
  expect(documents.collections).to eq [ :default ]
108
150
  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.2.0
4
+ version: 0.3.1
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-09-03 00:00:00.000000000 Z
11
+ date: 2024-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gem_hadar
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.17.0
19
+ version: 1.17.1
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.17.0
26
+ version: 1.17.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: all_images
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.7'
103
+ version: '0.8'
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.7'
110
+ version: '0.8'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: term-ansicolor
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -240,14 +240,14 @@ dependencies:
240
240
  requirements:
241
241
  - - "~>"
242
242
  - !ruby/object:Gem::Version
243
- version: '0.20'
243
+ version: '0.22'
244
244
  type: :runtime
245
245
  prerelease: false
246
246
  version_requirements: !ruby/object:Gem::Requirement
247
247
  requirements:
248
248
  - - "~>"
249
249
  - !ruby/object:Gem::Version
250
- version: '0.20'
250
+ version: '0.22'
251
251
  - !ruby/object:Gem::Dependency
252
252
  name: search_ui
253
253
  requirement: !ruby/object:Gem::Requirement
@@ -276,12 +276,27 @@ dependencies:
276
276
  - - "~>"
277
277
  - !ruby/object:Gem::Version
278
278
  version: 0.4.1
279
+ - !ruby/object:Gem::Dependency
280
+ name: pdf-reader
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - "~>"
284
+ - !ruby/object:Gem::Version
285
+ version: '2.0'
286
+ type: :runtime
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - "~>"
291
+ - !ruby/object:Gem::Version
292
+ version: '2.0'
279
293
  description: Library that allows interacting with the Ollama API
280
294
  email: flori@ping.de
281
295
  executables:
282
296
  - ollama_console
283
297
  - ollama_chat
284
298
  - ollama_update
299
+ - ollama_cli
285
300
  extensions: []
286
301
  extra_rdoc_files:
287
302
  - README.md
@@ -331,6 +346,7 @@ extra_rdoc_files:
331
346
  - lib/ollama/utils/chooser.rb
332
347
  - lib/ollama/utils/colorize_texts.rb
333
348
  - lib/ollama/utils/fetcher.rb
349
+ - lib/ollama/utils/file_argument.rb
334
350
  - lib/ollama/utils/math.rb
335
351
  - lib/ollama/utils/tags.rb
336
352
  - lib/ollama/utils/width.rb
@@ -343,6 +359,7 @@ files:
343
359
  - README.md
344
360
  - Rakefile
345
361
  - bin/ollama_chat
362
+ - bin/ollama_cli
346
363
  - bin/ollama_console
347
364
  - bin/ollama_update
348
365
  - config/redis.conf
@@ -393,6 +410,7 @@ files:
393
410
  - lib/ollama/utils/chooser.rb
394
411
  - lib/ollama/utils/colorize_texts.rb
395
412
  - lib/ollama/utils/fetcher.rb
413
+ - lib/ollama/utils/file_argument.rb
396
414
  - lib/ollama/utils/math.rb
397
415
  - lib/ollama/utils/tags.rb
398
416
  - lib/ollama/utils/width.rb
@@ -460,7 +478,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
460
478
  - !ruby/object:Gem::Version
461
479
  version: '0'
462
480
  requirements: []
463
- rubygems_version: 3.5.11
481
+ rubygems_version: 3.5.18
464
482
  signing_key:
465
483
  specification_version: 4
466
484
  summary: Interacting with the Ollama API