ollama-ruby 0.8.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +34 -0
- data/README.md +168 -166
- data/bin/ollama_chat +252 -204
- data/lib/ollama/handlers/markdown.rb +1 -2
- data/lib/ollama/utils/fetcher.rb +8 -0
- data/lib/ollama/version.rb +1 -1
- data/ollama-ruby.gemspec +4 -4
- data/spec/ollama/client_spec.rb +3 -3
- data/spec/ollama/documents/redis_backed_memory_cache_spec.rb +1 -1
- data/spec/ollama/documents/redis_cache_spec.rb +1 -1
- data/spec/ollama/documents_spec.rb +5 -5
- data/spec/ollama/handlers/markdown_spec.rb +0 -2
- data/spec/ollama/utils/fetcher_spec.rb +28 -6
- metadata +4 -4
data/bin/ollama_chat
CHANGED
@@ -52,6 +52,7 @@ class OllamaChatConfig
|
|
52
52
|
list: <%= `say -v ? 2>/dev/null`.lines.map { _1[/^(.+?)\s+[a-z]{2}_[a-zA-Z0-9]{2,}/, 1] }.uniq.sort.to_s.force_encoding('ASCII-8BIT') %>
|
53
53
|
markdown: true
|
54
54
|
stream: true
|
55
|
+
document_policy: importing
|
55
56
|
embedding:
|
56
57
|
enabled: true
|
57
58
|
model:
|
@@ -107,7 +108,7 @@ class OllamaChatConfig
|
|
107
108
|
end
|
108
109
|
|
109
110
|
class FollowChat
|
110
|
-
include
|
111
|
+
include Handlers::Concern
|
111
112
|
include Term::ANSIColor
|
112
113
|
|
113
114
|
def initialize(messages:, markdown: false, voice: nil, output: $stdout)
|
@@ -163,114 +164,142 @@ class FollowChat
|
|
163
164
|
end
|
164
165
|
end
|
165
166
|
|
166
|
-
module
|
167
|
-
|
167
|
+
module Switches
|
168
|
+
module CheckSwitch
|
169
|
+
extend Tins::Concern
|
168
170
|
|
169
|
-
|
170
|
-
|
171
|
-
|
171
|
+
included do
|
172
|
+
alias_method :on?, :value
|
173
|
+
end
|
172
174
|
|
173
|
-
|
174
|
-
|
175
|
-
|
175
|
+
def off?
|
176
|
+
!on?
|
177
|
+
end
|
176
178
|
|
177
|
-
|
178
|
-
|
179
|
+
def show
|
180
|
+
puts @msg[value]
|
181
|
+
end
|
179
182
|
end
|
180
|
-
end
|
181
183
|
|
182
|
-
class Switch
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
184
|
+
class Switch
|
185
|
+
def initialize(name, msg:, config: $config)
|
186
|
+
@value = [ false, true ].include?(config) ? config : !!config.send("#{name}?")
|
187
|
+
@msg = msg
|
188
|
+
end
|
187
189
|
|
188
|
-
|
190
|
+
attr_reader :value
|
189
191
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
192
|
+
def set(value, show: false)
|
193
|
+
@value = !!value
|
194
|
+
show && self.show
|
195
|
+
end
|
194
196
|
|
195
|
-
|
196
|
-
|
197
|
-
|
197
|
+
def toggle(show: true)
|
198
|
+
@value = !@value
|
199
|
+
show && self.show
|
200
|
+
end
|
201
|
+
|
202
|
+
include CheckSwitch
|
198
203
|
end
|
199
204
|
|
200
|
-
|
201
|
-
|
205
|
+
class CombinedSwitch
|
206
|
+
def initialize(value:, msg:)
|
207
|
+
@value = value
|
208
|
+
@msg = msg
|
209
|
+
end
|
202
210
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
@msg = msg
|
207
|
-
end
|
211
|
+
def value
|
212
|
+
@value.()
|
213
|
+
end
|
208
214
|
|
209
|
-
|
210
|
-
@value.()
|
215
|
+
include CheckSwitch
|
211
216
|
end
|
212
217
|
|
213
|
-
|
214
|
-
|
218
|
+
def setup_switches
|
219
|
+
$markdown = Switch.new(
|
220
|
+
:markdown,
|
221
|
+
msg: {
|
222
|
+
true => "Using #{italic{'ANSI'}} markdown to output content.",
|
223
|
+
false => "Using plaintext for outputting content.",
|
224
|
+
}
|
225
|
+
)
|
215
226
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
)
|
227
|
+
$stream = Switch.new(
|
228
|
+
:stream,
|
229
|
+
msg: {
|
230
|
+
true => "Streaming enabled.",
|
231
|
+
false => "Streaming disabled.",
|
232
|
+
}
|
233
|
+
)
|
224
234
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
235
|
+
$voice = Switch.new(
|
236
|
+
:stream,
|
237
|
+
msg: {
|
238
|
+
true => "Voice output enabled.",
|
239
|
+
false => "Voice output disabled.",
|
240
|
+
},
|
241
|
+
config: $config.voice
|
242
|
+
)
|
232
243
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
)
|
244
|
+
$embedding_enabled = Switch.new(
|
245
|
+
:embedding_enabled,
|
246
|
+
msg: {
|
247
|
+
true => "Embedding enabled.",
|
248
|
+
false => "Embedding disabled.",
|
249
|
+
}
|
250
|
+
)
|
241
251
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
252
|
+
$embedding_paused = Switch.new(
|
253
|
+
:embedding_paused,
|
254
|
+
msg: {
|
255
|
+
true => "Embedding paused.",
|
256
|
+
false => "Embedding resumed.",
|
257
|
+
}
|
258
|
+
)
|
249
259
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
260
|
+
$embedding = CombinedSwitch.new(
|
261
|
+
value: -> { $embedding_enabled.on? && $embedding_paused.off? },
|
262
|
+
msg: {
|
263
|
+
true => "Embedding is currently performed.",
|
264
|
+
false => "Embedding is currently not performed.",
|
265
|
+
}
|
266
|
+
)
|
257
267
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
268
|
+
$location = Switch.new(
|
269
|
+
:location,
|
270
|
+
msg: {
|
271
|
+
true => "Location and localtime enabled.",
|
272
|
+
false => "Location and localtime disabled.",
|
273
|
+
},
|
274
|
+
config: $config.location.enabled
|
275
|
+
)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
include Switches
|
265
279
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
280
|
+
def pull_model_unless_present(model, options, retried = false)
|
281
|
+
ollama.show(name: model) { |response|
|
282
|
+
puts "Model #{bold{model}} with architecture "\
|
283
|
+
"#{response.model_info['general.architecture']} found."
|
284
|
+
if system = response.system
|
285
|
+
puts "Configured model system prompt is:\n#{italic { system }}"
|
286
|
+
return system
|
287
|
+
else
|
288
|
+
return
|
289
|
+
end
|
290
|
+
}
|
291
|
+
rescue Errors::NotFoundError
|
292
|
+
puts "Model #{bold{model}} not found locally, attempting to pull it from remote now…"
|
293
|
+
ollama.pull(name: model)
|
294
|
+
if retried
|
295
|
+
exit 1
|
296
|
+
else
|
297
|
+
retried = true
|
298
|
+
retry
|
299
|
+
end
|
300
|
+
rescue Errors::Error => e
|
301
|
+
warn "Caught #{e.class} while pulling model: #{e} => Exiting."
|
302
|
+
exit 1
|
274
303
|
end
|
275
304
|
|
276
305
|
def search_web(query, n = nil)
|
@@ -281,7 +310,7 @@ def search_web(query, n = nil)
|
|
281
310
|
n < 1 and n = 1
|
282
311
|
query = URI.encode_uri_component(query)
|
283
312
|
url = "https://www.duckduckgo.com/html/?q=#{query}"
|
284
|
-
|
313
|
+
Utils::Fetcher.get(url, debug: $config.debug) do |tmp|
|
285
314
|
result = []
|
286
315
|
doc = Nokogiri::HTML(tmp)
|
287
316
|
doc.css('.results_links').each do |link|
|
@@ -302,38 +331,13 @@ def search_web(query, n = nil)
|
|
302
331
|
end
|
303
332
|
end
|
304
333
|
|
305
|
-
def pull_model_unless_present(model, options, retried = false)
|
306
|
-
ollama.show(name: model) { |response|
|
307
|
-
puts "Model #{bold{model}} with architecture "\
|
308
|
-
"#{response.model_info['general.architecture']} found."
|
309
|
-
if system = response.system
|
310
|
-
puts "Configured model system prompt is:\n#{italic { system }}"
|
311
|
-
return system
|
312
|
-
else
|
313
|
-
return
|
314
|
-
end
|
315
|
-
}
|
316
|
-
rescue Errors::NotFoundError
|
317
|
-
puts "Model #{bold{model}} not found locally, attempting to pull it from remote now…"
|
318
|
-
ollama.pull(name: model)
|
319
|
-
if retried
|
320
|
-
exit 1
|
321
|
-
else
|
322
|
-
retried = true
|
323
|
-
retry
|
324
|
-
end
|
325
|
-
rescue Errors::Error => e
|
326
|
-
warn "Caught #{e.class} while pulling model: #{e} => Exiting."
|
327
|
-
exit 1
|
328
|
-
end
|
329
|
-
|
330
334
|
def load_conversation(filename)
|
331
335
|
unless File.exist?(filename)
|
332
336
|
puts "File #{filename} doesn't exist. Choose another filename."
|
333
337
|
return
|
334
338
|
end
|
335
339
|
File.open(filename, 'r') do |output|
|
336
|
-
return JSON(output.read).map {
|
340
|
+
return JSON(output.read).map { Message.from_hash(_1) }
|
337
341
|
end
|
338
342
|
end
|
339
343
|
|
@@ -479,59 +483,6 @@ def parse_source(source_io)
|
|
479
483
|
end
|
480
484
|
end
|
481
485
|
|
482
|
-
def embed_source(source_io, source, count: nil)
|
483
|
-
$embedding.on? or return parse_source(source_io)
|
484
|
-
m = "Embedding #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
|
485
|
-
if count
|
486
|
-
puts '%u. %s' % [ count, m ]
|
487
|
-
else
|
488
|
-
puts m
|
489
|
-
end
|
490
|
-
text = parse_source(source_io) or return
|
491
|
-
text.downcase!
|
492
|
-
splitter_config = $config.embedding.splitter
|
493
|
-
inputs = nil
|
494
|
-
case splitter_config.name
|
495
|
-
when 'Character'
|
496
|
-
splitter = Ollama::Documents::Splitters::Character.new(
|
497
|
-
chunk_size: splitter_config.chunk_size,
|
498
|
-
)
|
499
|
-
inputs = splitter.split(text)
|
500
|
-
when 'RecursiveCharacter'
|
501
|
-
splitter = Ollama::Documents::Splitters::RecursiveCharacter.new(
|
502
|
-
chunk_size: splitter_config.chunk_size,
|
503
|
-
)
|
504
|
-
inputs = splitter.split(text)
|
505
|
-
when 'Semantic'
|
506
|
-
splitter = Ollama::Documents::Splitters::Semantic.new(
|
507
|
-
ollama:, model: $config.embedding.model.name,
|
508
|
-
chunk_size: splitter_config.chunk_size,
|
509
|
-
)
|
510
|
-
inputs = splitter.split(
|
511
|
-
text,
|
512
|
-
breakpoint: splitter_config.breakpoint.to_sym,
|
513
|
-
percentage: splitter_config.percentage?,
|
514
|
-
percentile: splitter_config.percentile?,
|
515
|
-
)
|
516
|
-
inputs = splitter.split(text)
|
517
|
-
end
|
518
|
-
inputs or return
|
519
|
-
source = source.to_s
|
520
|
-
if source.start_with?(?!)
|
521
|
-
source = Ollama::Utils::Width.truncate(
|
522
|
-
source[1..-1].gsub(/\W+/, ?_),
|
523
|
-
length: 10
|
524
|
-
)
|
525
|
-
end
|
526
|
-
$documents.add(inputs, source:, batch_size: $config.embedding.batch_size?)
|
527
|
-
end
|
528
|
-
|
529
|
-
def add_image(images, source_io, source)
|
530
|
-
STDERR.puts "Adding #{source_io&.content_type} image #{source.to_s.inspect}."
|
531
|
-
image = Image.for_io(source_io, path: source.to_s)
|
532
|
-
(images << image).uniq!
|
533
|
-
end
|
534
|
-
|
535
486
|
def http_options(url)
|
536
487
|
options = {}
|
537
488
|
if ssl_no_verify = $config.ssl_no_verify?
|
@@ -556,7 +507,7 @@ def fetch_source(source, &block)
|
|
556
507
|
source,
|
557
508
|
cache: $cache,
|
558
509
|
debug: $config.debug,
|
559
|
-
http_options: http_options(source)
|
510
|
+
http_options: http_options(Utils::Fetcher.normalize_url(source))
|
560
511
|
) do |tmp|
|
561
512
|
block.(tmp)
|
562
513
|
end
|
@@ -570,33 +521,94 @@ def fetch_source(source, &block)
|
|
570
521
|
raise "invalid source"
|
571
522
|
end
|
572
523
|
rescue => e
|
573
|
-
STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e}\n#{e.backtrace * ?\n}"
|
524
|
+
STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e.class} #{e}\n#{e.backtrace * ?\n}"
|
525
|
+
end
|
526
|
+
|
527
|
+
def add_image(images, source_io, source)
|
528
|
+
STDERR.puts "Adding #{source_io&.content_type} image #{source.to_s.inspect}."
|
529
|
+
image = Image.for_io(source_io, path: source.to_s)
|
530
|
+
(images << image).uniq!
|
531
|
+
end
|
532
|
+
|
533
|
+
def import_source(source_io, source)
|
534
|
+
source = source.to_s
|
535
|
+
puts "Importing #{italic { source_io&.content_type }} document #{source.inspect} now."
|
536
|
+
source_content = parse_source(source_io)
|
537
|
+
"Imported #{source.inspect}:\n#{source_content}\n\n"
|
574
538
|
end
|
575
539
|
|
576
540
|
def import(source)
|
577
|
-
puts "Now importing #{source.to_s.inspect}."
|
578
541
|
fetch_source(source) do |source_io|
|
579
|
-
content =
|
580
|
-
content.present? or return
|
542
|
+
content = import_source(source_io, source) or return
|
581
543
|
source_io.rewind
|
582
544
|
content
|
583
545
|
end
|
584
546
|
end
|
585
547
|
|
586
|
-
def
|
548
|
+
def summarize_source(source_io, source, words: nil)
|
549
|
+
puts "Summarizing #{italic { source_io&.content_type }} document #{source.inspect} now."
|
587
550
|
words = words.to_i
|
588
551
|
words < 1 and words = 100
|
589
|
-
|
590
|
-
source_content
|
591
|
-
fetch_source(source) do |source_io|
|
592
|
-
content = parse_source(source_io)
|
593
|
-
content.present? or return
|
594
|
-
source_io.rewind
|
595
|
-
content
|
596
|
-
end
|
552
|
+
source_content = parse_source(source_io)
|
553
|
+
source_content.present? or return
|
597
554
|
$config.prompts.summarize % { source_content:, words: }
|
598
555
|
end
|
599
556
|
|
557
|
+
def summarize(source, words: nil)
|
558
|
+
fetch_source(source) do |source_io|
|
559
|
+
content = summarize_source(source_io, source, words:) or return
|
560
|
+
source_io.rewind
|
561
|
+
content
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def embed_source(source_io, source, count: nil)
|
566
|
+
$embedding.on? or return parse_source(source_io)
|
567
|
+
m = "Embedding #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
|
568
|
+
if count
|
569
|
+
puts '%u. %s' % [ count, m ]
|
570
|
+
else
|
571
|
+
puts m
|
572
|
+
end
|
573
|
+
text = parse_source(source_io) or return
|
574
|
+
text.downcase!
|
575
|
+
splitter_config = $config.embedding.splitter
|
576
|
+
inputs = nil
|
577
|
+
case splitter_config.name
|
578
|
+
when 'Character'
|
579
|
+
splitter = Documents::Splitters::Character.new(
|
580
|
+
chunk_size: splitter_config.chunk_size,
|
581
|
+
)
|
582
|
+
inputs = splitter.split(text)
|
583
|
+
when 'RecursiveCharacter'
|
584
|
+
splitter = Documents::Splitters::RecursiveCharacter.new(
|
585
|
+
chunk_size: splitter_config.chunk_size,
|
586
|
+
)
|
587
|
+
inputs = splitter.split(text)
|
588
|
+
when 'Semantic'
|
589
|
+
splitter = Documents::Splitters::Semantic.new(
|
590
|
+
ollama:, model: $config.embedding.model.name,
|
591
|
+
chunk_size: splitter_config.chunk_size,
|
592
|
+
)
|
593
|
+
inputs = splitter.split(
|
594
|
+
text,
|
595
|
+
breakpoint: splitter_config.breakpoint.to_sym,
|
596
|
+
percentage: splitter_config.percentage?,
|
597
|
+
percentile: splitter_config.percentile?,
|
598
|
+
)
|
599
|
+
inputs = splitter.split(text)
|
600
|
+
end
|
601
|
+
inputs or return
|
602
|
+
source = source.to_s
|
603
|
+
if source.start_with?(?!)
|
604
|
+
source = Utils::Width.truncate(
|
605
|
+
source[1..-1].gsub(/\W+/, ?_),
|
606
|
+
length: 10
|
607
|
+
)
|
608
|
+
end
|
609
|
+
$documents.add(inputs, source:, batch_size: $config.embedding.batch_size?)
|
610
|
+
end
|
611
|
+
|
600
612
|
def embed(source)
|
601
613
|
if $embedding.on?
|
602
614
|
puts "Now embedding #{source.to_s.inspect}."
|
@@ -618,6 +630,7 @@ def parse_content(content, images)
|
|
618
630
|
images.clear
|
619
631
|
tags = Utils::Tags.new
|
620
632
|
|
633
|
+
contents = [ content ]
|
621
634
|
content.scan(%r((?:\.\.|[.~])?/\S+|https?://\S+|#\S+)).each do |source|
|
622
635
|
case source
|
623
636
|
when /\A#(\S+)/
|
@@ -628,8 +641,15 @@ def parse_content(content, images)
|
|
628
641
|
case source_io&.content_type&.media_type
|
629
642
|
when 'image'
|
630
643
|
add_image(images, source_io, source)
|
631
|
-
when 'text', 'application'
|
632
|
-
|
644
|
+
when 'text', 'application', nil
|
645
|
+
case $document_policy
|
646
|
+
when 'importing'
|
647
|
+
contents << import_source(source_io, source)
|
648
|
+
when 'embedding'
|
649
|
+
embed_source(source_io, source)
|
650
|
+
when 'summarizing'
|
651
|
+
contents << summarize_source(source_io, source)
|
652
|
+
end
|
633
653
|
else
|
634
654
|
STDERR.puts(
|
635
655
|
"Cannot fetch #{source.to_s.inspect} with content type "\
|
@@ -639,14 +659,14 @@ def parse_content(content, images)
|
|
639
659
|
end
|
640
660
|
end
|
641
661
|
end
|
642
|
-
|
643
|
-
return
|
662
|
+
new_content = contents.select(&:present?).compact * "\n\n"
|
663
|
+
return new_content, (tags unless tags.empty?)
|
644
664
|
end
|
645
665
|
|
646
666
|
def choose_model(cli_model, current_model)
|
647
667
|
models = ollama.tags.models.map(&:name).sort
|
648
668
|
model = if cli_model == ''
|
649
|
-
|
669
|
+
Utils::Chooser.choose(models) || current_model
|
650
670
|
else
|
651
671
|
cli_model || current_model
|
652
672
|
end
|
@@ -663,7 +683,7 @@ def choose_collection(current_collection)
|
|
663
683
|
collections = [ current_collection ] + $documents.collections
|
664
684
|
collections = collections.compact.map(&:to_s).uniq.sort
|
665
685
|
collections.unshift('[EXIT]').unshift('[NEW]')
|
666
|
-
collection =
|
686
|
+
collection = Utils::Chooser.choose(collections) || current_collection
|
667
687
|
case collection
|
668
688
|
when '[NEW]'
|
669
689
|
$documents.collection = ask?(prompt: "Enter name of the new collection: ")
|
@@ -674,7 +694,29 @@ def choose_collection(current_collection)
|
|
674
694
|
end
|
675
695
|
ensure
|
676
696
|
puts "Using collection #{bold{$documents.collection}}."
|
677
|
-
|
697
|
+
info
|
698
|
+
end
|
699
|
+
|
700
|
+
def choose_document_policy
|
701
|
+
policies = %w[ importing embedding summarizing ].sort
|
702
|
+
current = if policies.index($document_policy)
|
703
|
+
$document_policy
|
704
|
+
elsif policies.index($config.document_policy)
|
705
|
+
$config.document_policy
|
706
|
+
else
|
707
|
+
policies.first
|
708
|
+
end
|
709
|
+
policies.unshift('[EXIT]')
|
710
|
+
policy = Utils::Chooser.choose(policies)
|
711
|
+
case policy
|
712
|
+
when nil, '[EXIT]'
|
713
|
+
puts "Exiting chooser."
|
714
|
+
policy = current
|
715
|
+
end
|
716
|
+
$document_policy = policy
|
717
|
+
ensure
|
718
|
+
puts "Using document policy #{bold{$document_policy}}."
|
719
|
+
info
|
678
720
|
end
|
679
721
|
|
680
722
|
def collection_stats
|
@@ -695,19 +737,19 @@ end
|
|
695
737
|
|
696
738
|
def configure_cache
|
697
739
|
if $opts[?M]
|
698
|
-
|
740
|
+
Documents::MemoryCache
|
699
741
|
else
|
700
742
|
Object.const_get($config.cache)
|
701
743
|
end
|
702
744
|
rescue => e
|
703
745
|
STDERR.puts "Caught #{e.class}: #{e} => Falling back to MemoryCache."
|
704
|
-
|
746
|
+
Documents::MemoryCache
|
705
747
|
end
|
706
748
|
|
707
749
|
def show_system_prompt
|
708
750
|
puts <<~EOT
|
709
751
|
Configured system prompt is:
|
710
|
-
#{
|
752
|
+
#{Utils::ANSIMarkdown.parse($system.to_s).gsub(/\n+\z/, '').full? || 'n/a'}
|
711
753
|
EOT
|
712
754
|
end
|
713
755
|
|
@@ -731,7 +773,7 @@ end
|
|
731
773
|
|
732
774
|
def change_system_prompt(messages, default)
|
733
775
|
prompts = $config.system_prompts.attribute_names.compact
|
734
|
-
chosen =
|
776
|
+
chosen = Utils::Chooser.choose(prompts)
|
735
777
|
system = if chosen
|
736
778
|
$config.system_prompts.send(chosen)
|
737
779
|
else
|
@@ -741,7 +783,7 @@ def change_system_prompt(messages, default)
|
|
741
783
|
end
|
742
784
|
|
743
785
|
def change_voice
|
744
|
-
chosen =
|
786
|
+
chosen = Utils::Chooser.choose($config.voice.list)
|
745
787
|
$current_voice = chosen.full? || $config.voice.default
|
746
788
|
end
|
747
789
|
|
@@ -756,6 +798,7 @@ def info
|
|
756
798
|
$markdown.show
|
757
799
|
$stream.show
|
758
800
|
$location.show
|
801
|
+
puts "Document policy for references in user text: #{bold{$document_policy}}"
|
759
802
|
if $voice.on?
|
760
803
|
puts "Using voice #{bold{$current_voice}} to speak."
|
761
804
|
end
|
@@ -799,6 +842,7 @@ def display_chat_help
|
|
799
842
|
/regenerate the last answer message
|
800
843
|
/collection( clear|change) change (default) collection or clear
|
801
844
|
/info show information for current session
|
845
|
+
/document_policy pick a scan policy for document references
|
802
846
|
/import source import the source's content
|
803
847
|
/summarize [n] source summarize the source's content in n words
|
804
848
|
/embedding toggle embedding paused or not
|
@@ -832,7 +876,7 @@ def usage
|
|
832
876
|
end
|
833
877
|
|
834
878
|
def version
|
835
|
-
puts "%s %s" % [ File.basename($0),
|
879
|
+
puts "%s %s" % [ File.basename($0), VERSION ]
|
836
880
|
exit 0
|
837
881
|
end
|
838
882
|
|
@@ -853,10 +897,11 @@ $opts[?V] and version
|
|
853
897
|
base_url = $opts[?u] || $config.url
|
854
898
|
$ollama = Client.new(base_url:, debug: $config.debug)
|
855
899
|
|
856
|
-
$
|
857
|
-
|
858
|
-
|
859
|
-
|
900
|
+
$document_policy = $config.document_policy
|
901
|
+
$model = choose_model($opts[?m], $config.model.name)
|
902
|
+
options = Options[$config.model.options]
|
903
|
+
model_system = pull_model_unless_present($model, options)
|
904
|
+
messages = []
|
860
905
|
$embedding_enabled.set($config.embedding.enabled && !$opts[?E])
|
861
906
|
|
862
907
|
if $opts[?c]
|
@@ -866,7 +911,7 @@ else
|
|
866
911
|
if $opts[?s] == ??
|
867
912
|
change_system_prompt(messages, default)
|
868
913
|
else
|
869
|
-
system =
|
914
|
+
system = Utils::FileArgument.get_file_argument($opts[?s], default:)
|
870
915
|
system.present? and set_system_prompt(messages, system)
|
871
916
|
end
|
872
917
|
end
|
@@ -916,7 +961,7 @@ else
|
|
916
961
|
end
|
917
962
|
|
918
963
|
if redis_expiring_url = $config.redis.expiring.url?
|
919
|
-
$cache =
|
964
|
+
$cache = Documents::RedisCache.new(
|
920
965
|
prefix: 'Expiring-',
|
921
966
|
url: redis_expiring_url,
|
922
967
|
ex: $config.redis.expiring.ex,
|
@@ -969,7 +1014,7 @@ loop do
|
|
969
1014
|
puts "Cleared messages."
|
970
1015
|
next
|
971
1016
|
when %r(^/clobber$)
|
972
|
-
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
1017
|
+
if ask?(prompt: 'Are you sure to clear messages and collection? (y/n) ') =~ /\Ay/i
|
973
1018
|
clear_messages(messages)
|
974
1019
|
$documents.clear
|
975
1020
|
puts "Cleared messages and collection #{bold{$documents.collection}}."
|
@@ -1010,7 +1055,7 @@ loop do
|
|
1010
1055
|
when 'clear'
|
1011
1056
|
loop do
|
1012
1057
|
tags = $documents.tags.add('[EXIT]').add('[ALL]')
|
1013
|
-
tag =
|
1058
|
+
tag = Utils::Chooser.choose(tags, prompt: 'Clear? %s')
|
1014
1059
|
case tag
|
1015
1060
|
when nil, '[EXIT]'
|
1016
1061
|
puts "Exiting chooser."
|
@@ -1034,9 +1079,12 @@ loop do
|
|
1034
1079
|
choose_collection($documents.collection)
|
1035
1080
|
end
|
1036
1081
|
next
|
1037
|
-
when %r(
|
1082
|
+
when %r(^/info$)
|
1038
1083
|
info
|
1039
1084
|
next
|
1085
|
+
when %r(^/document_policy$)
|
1086
|
+
choose_document_policy
|
1087
|
+
next
|
1040
1088
|
when %r(^/import\s+(.+))
|
1041
1089
|
parse_content = false
|
1042
1090
|
content = import($1) or next
|
@@ -7,7 +7,7 @@ class Ollama::Handlers::Markdown
|
|
7
7
|
def initialize(output: $stdout)
|
8
8
|
super
|
9
9
|
@output.sync = true
|
10
|
-
@content
|
10
|
+
@content = ''
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(response)
|
@@ -16,7 +16,6 @@ class Ollama::Handlers::Markdown
|
|
16
16
|
markdown_content = Ollama::Utils::ANSIMarkdown.parse(@content)
|
17
17
|
@output.print clear_screen, move_home, markdown_content
|
18
18
|
end
|
19
|
-
response.done and @output.puts
|
20
19
|
self
|
21
20
|
end
|
22
21
|
end
|