ollama-ruby 0.2.0 → 0.3.1

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: 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