ollama-ruby 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a6a94882e3f76e84fbceadd80ec844f9a93febd18ed294b01f7874867c53e68
4
- data.tar.gz: 47cda3eef10438ff1cd136ffcc4469d2275bc2e26cdab985c7a001e2d0178b7f
3
+ metadata.gz: d0b3ea2bd67e3f4fa2ca4da88a25cb869fc81e35578f1e7fd97ba43e84fc2278
4
+ data.tar.gz: 626c534bd2fd0580ab0520e8b70587489edbc963e179be0f800ee63235690b33
5
5
  SHA512:
6
- metadata.gz: 5c42f47be513e41465693c47dee68447ac42a967f7ecaaf88ea51df6ea30eb9beb61b25595fc145909a0c9b8838d22756f47e71bc6acdd33bb47e4876545ccdc
7
- data.tar.gz: b4ac6bf59882a23774fbfb3503607c1a22d200037b31613a8f9d565ebf174b99d66259cc62d99f0635e7ac99fd07cc51aec735dd7dbb31259dc767e403eaa5d7
6
+ metadata.gz: 149986d8144b1bc2c20771e245ead20f6e6bc07b46dbe26f8f355e0bc61eca146cc085a920dcbe3e3e08de65632693a35b6995c9dffef288aa929dd6b034de16
7
+ data.tar.gz: 4214aa2911db561635011b8ea200074d15c74b93bf7abb3c8e77af9c40ac5d8994e908e2ade8e5f288c2919678e03ea5d84d07684a63ba1f8039de52b6091a86
data/CHANGES.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changes
2
2
 
3
+ ## 2024-10-18 v0.9.0
4
+
5
+ * Add document policy chooser and modify embedding/importing/summarizing
6
+ behavior:
7
+ + Add `/document_policy` command to choose a scan policy for document
8
+ references
9
+ + Modify `embed_source`, `import_source`, and `summarize_source` methods to
10
+ use the chosen document policy
11
+ + Update `choose_model` method to set `$document_policy` based on
12
+ configuration or chat command
13
+ * Fix regular expression in `ollama_chat` script:
14
+ + Updated regular expression for `/info` to `/^/info$`
15
+ * Improve ask prompt to ask about clearing messages and collection.
16
+ * Update specs to use `expect` instead of `allow`
17
+ * Fix library homepage URL in README.md
18
+ * Refactor Markdown handler to remove unnecessary puts statement
19
+ * Reorder chapters in README.md a bit
20
+
3
21
  ## 2024-10-07 v0.8.0
4
22
 
5
23
  * **Refactor source handling in Ollama chat**:
data/README.md CHANGED
@@ -26,161 +26,23 @@ gem 'ollama-ruby'
26
26
 
27
27
  to your Gemfile and run `bundle install` in your terminal.
28
28
 
29
- ## Executables
30
-
31
- ### ollama\_chat
32
-
33
- This a chat client, that can be used to connect to an ollama server and enter a
34
- chat converstation with a LLM. It can be called with the following arguments:
35
-
36
- ```
37
- Usage: ollama_chat [OPTIONS]
38
-
39
- -f CONFIG config file to read
40
- -u URL the ollama base url, OLLAMA_URL
41
- -m MODEL the ollama model to chat with, OLLAMA_CHAT_MODEL
42
- -s SYSTEM the system prompt to use as a file, OLLAMA_CHAT_SYSTEM
43
- -c CHAT a saved chat conversation to load
44
- -C COLLECTION name of the collection used in this conversation
45
- -D DOCUMENT load document and add to embeddings collection (multiple)
46
- -M use (empty) MemoryCache for this chat session
47
- -E disable embeddings for this chat session
48
- -V display the current version number and quit
49
- -h this help
50
- ```
51
-
52
- The base URL can be either set by the environment variable `OLLAMA_URL` or it
53
- is derived from the environment variable `OLLAMA_HOST`. The default model to
54
- connect can be configured in the environment variable `OLLAMA_MODEL`.
55
-
56
- The YAML config file in `$XDG_CONFIG_HOME/ollama_chat/config.yml`, that you can
57
- use for more complex settings, it looks like this:
58
-
59
- ```
60
- ---
61
- url: <%= ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST') %>
62
- model:
63
- name: <%= ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1') %>
64
- options:
65
- num_ctx: 8192
66
- system: <%= ENV.fetch('OLLAMA_CHAT_SYSTEM', 'null') %>
67
- voice: Samantha
68
- markdown: true
69
- embedding:
70
- enabled: true
71
- model:
72
- name: mxbai-embed-large
73
- options: {}
74
- collection: <%= ENV.fetch('OLLAMA_CHAT_COLLECTION', 'ollama_chat') %>
75
- found_texts_size: 4096
76
- splitter:
77
- name: RecursiveCharacter
78
- chunk_size: 1024
79
- cache: Ollama::Documents::RedisCache
80
- redis:
81
- url: <%= ENV.fetch('REDIS_URL', 'null') %>
82
- debug: <%= ENV['OLLAMA_CHAT_DEBUG'].to_i == 1 ? true : false %>
83
- ```
84
-
85
- If you want to store embeddings persistently, set an environment variable
86
- `REDIS_URL` or update the `redis.url` setting in your `config.yml` file to
87
- connect to a Redis server. Without this setup, embeddings will only be stored
88
- in process memory, which is less durable.
89
-
90
- Some settings can be passed as arguments as well, e. g. if you want to choose a
91
- specific system prompt:
92
-
93
- ```
94
- $ ollama_chat -s sherlock.txt
95
- Model with architecture llama found.
96
- Connecting to llama3.1@http://ollama.local.net:11434 now…
97
- Configured system prompt is:
98
- You are Sherlock Holmes and the user is your new client, Dr. Watson is also in
99
- the room. You will talk and act in the typical manner of Sherlock Holmes do and
100
- try to solve the user's case using logic and deduction.
101
-
102
- Type /help to display the chat help.
103
- 📨 user:
104
- Good morning.
105
- 📨 assistant:
106
- Ah, good morning, my dear fellow! It is a pleasure to make your acquaintance. I
107
- am Sherlock Holmes, the renowned detective, and this is my trusty sidekick, Dr.
108
- Watson. Please, have a seat and tell us about the nature of your visit. What
109
- seems to be the problem that has brought you to our humble abode at 221B Baker
110
- Street?
111
-
112
- (Watson nods in encouragement as he takes notes)
113
-
114
- Now, pray tell, what is it that puzzles you, my dear client? A missing item,
115
- perhaps? Or a mysterious occurrence that requires clarification? The game, as
116
- they say, is afoot!
117
- ```
118
-
119
- This example shows how an image like this can be sent to a vision model for
120
- analysis:
121
-
122
- ![cat](spec/assets/kitten.jpg)
123
-
124
- ```
125
- $ ollama_chat -m llava-llama3
126
- Model with architecture llama found.
127
- Connecting to llava-llama3@http://localhost:11434 now…
128
- Type /help to display the chat help.
129
- 📸 user> What's on this image? ./spec/assets/kitten.jpg
130
- 📨 assistant:
131
- The image captures a moment of tranquility featuring a young cat. The cat,
132
- adorned with gray and white fur marked by black stripes on its face and legs,
133
- is the central figure in this scene. Its eyes, a striking shade of blue, are
134
- wide open and directed towards the camera, giving an impression of curiosity or
135
- alertness.
136
-
137
- The cat is comfortably nestled on a red blanket, which contrasts vividly with
138
- its fur. The blanket, soft and inviting, provides a sense of warmth to the
139
- image. In the background, partially obscured by the cat's head, is another
140
- blanket of similar red hue. The repetition of the color adds a sense of harmony
141
- to the composition.
142
-
143
- The cat's position on the right side of the photo creates an interesting
144
- asymmetry with the camera lens, which occupies the left side of the frame. This
145
- visual balance enhances the overall composition of the image.
29
+ ## Usage
146
30
 
147
- There are no discernible texts or other objects in the image. The focus is
148
- solely on the cat and its immediate surroundings. The image does not provide
149
- any information about the location or setting beyond what has been described.
150
- The simplicity of the scene allows the viewer to concentrate on the main
151
- subject - the young, blue-eyed cat.
152
- ```
31
+ In your own software the library can be used as shown in this example:
153
32
 
154
- The following commands can be given inside the chat, if prefixed by a `/`:
33
+ ```ruby
34
+ require "ollama"
35
+ include Ollama
155
36
 
156
- ```
157
- /copy to copy last response to clipboard
158
- /paste to paste content
159
- /markdown toggle markdown output
160
- /stream toggle stream output
161
- /location toggle location submission
162
- /voice( change) toggle voice output or change the voice
163
- /list [n] list the last n / all conversation exchanges
164
- /clear clear the whole conversation
165
- /clobber clear the conversation and collection
166
- /pop [n] pop the last n exchanges, defaults to 1
167
- /model change the model
168
- /system change system prompt (clears conversation)
169
- /regenerate the last answer message
170
- /collection( clear|change) change (default) collection or clear
171
- /info show information for current session
172
- /import source import the source's content
173
- /summarize [n] source summarize the source's content in n words
174
- /embedding toggle embedding paused or not
175
- /embed source embed the source's content
176
- /web [n] query query web search & return n or 1 results
177
- /save filename store conversation messages
178
- /load filename load conversation messages
179
- /quit to quit
180
- /help to view this help
37
+ ollama = Client.new(base_url: 'http://localhost:11434')
38
+ messages = Message.new(role: 'user', content: 'Why is the sky blue?')
39
+ ollama.chat(model: 'llama3.1', stream: true, messages:, &Print) # or
40
+ print ollama.chat(model: 'llama3.1', stream: true, messages:).lazy.map { |response|
41
+ response.message.content
42
+ }
181
43
  ```
182
44
 
183
- ### ollama\_console
45
+ ## Try out things in ollama\_console
184
46
 
185
47
  This is an interactive console, that can be used to try the different commands
186
48
  provided by an `Ollama::Client` instance. For example this command generate a
@@ -197,21 +59,6 @@ Commands: chat,copy,create,delete,embeddings,generate,help,ps,pull,push,show,tag
197
59
  > In a small village nestled between two great palm trees 🌳, there lived a
198
60
  > brave adventurer named Alex 👦. […]
199
61
 
200
- ## Usage
201
-
202
- In your own software the library can be used as shown in this example:
203
-
204
- ```ruby
205
- require "ollama"
206
- include Ollama
207
-
208
- ollama = Client.new(base_url: 'http://localhost:11434')
209
- messages = Message.new(role: 'user', content: 'Why is the sky blue?')
210
- ollama.chat(model: 'llama3.1', stream: true, messages:, &Print) # or
211
- print ollama.chat(model: 'llama3.1', stream: true, messages:).lazy.map { |response|
212
- response.message.content
213
- }
214
- ```
215
62
 
216
63
  ## API
217
64
 
@@ -463,11 +310,166 @@ If `Ollama::Errors::TimeoutError` is raised, it might help to increase the
463
310
 
464
311
  For more generic errors an `Ollama::Errors::Error` is raised.
465
312
 
313
+ ## Other executables
314
+
315
+ ### ollama\_chat
316
+
317
+ This a chat client, that can be used to connect to an ollama server and enter a
318
+ chat converstation with a LLM. It can be called with the following arguments:
319
+
320
+ ```
321
+ Usage: ollama_chat [OPTIONS]
322
+
323
+ -f CONFIG config file to read
324
+ -u URL the ollama base url, OLLAMA_URL
325
+ -m MODEL the ollama model to chat with, OLLAMA_CHAT_MODEL
326
+ -s SYSTEM the system prompt to use as a file, OLLAMA_CHAT_SYSTEM
327
+ -c CHAT a saved chat conversation to load
328
+ -C COLLECTION name of the collection used in this conversation
329
+ -D DOCUMENT load document and add to embeddings collection (multiple)
330
+ -M use (empty) MemoryCache for this chat session
331
+ -E disable embeddings for this chat session
332
+ -V display the current version number and quit
333
+ -h this help
334
+ ```
335
+
336
+ The base URL can be either set by the environment variable `OLLAMA_URL` or it
337
+ is derived from the environment variable `OLLAMA_HOST`. The default model to
338
+ connect can be configured in the environment variable `OLLAMA_MODEL`.
339
+
340
+ The YAML config file in `$XDG_CONFIG_HOME/ollama_chat/config.yml`, that you can
341
+ use for more complex settings, it looks like this:
342
+
343
+ ```
344
+ ---
345
+ url: <%= ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST') %>
346
+ model:
347
+ name: <%= ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1') %>
348
+ options:
349
+ num_ctx: 8192
350
+ system: <%= ENV.fetch('OLLAMA_CHAT_SYSTEM', 'null') %>
351
+ voice: Samantha
352
+ markdown: true
353
+ embedding:
354
+ enabled: true
355
+ model:
356
+ name: mxbai-embed-large
357
+ options: {}
358
+ collection: <%= ENV.fetch('OLLAMA_CHAT_COLLECTION', 'ollama_chat') %>
359
+ found_texts_size: 4096
360
+ splitter:
361
+ name: RecursiveCharacter
362
+ chunk_size: 1024
363
+ cache: Ollama::Documents::RedisCache
364
+ redis:
365
+ url: <%= ENV.fetch('REDIS_URL', 'null') %>
366
+ debug: <%= ENV['OLLAMA_CHAT_DEBUG'].to_i == 1 ? true : false %>
367
+ ```
368
+
369
+ If you want to store embeddings persistently, set an environment variable
370
+ `REDIS_URL` or update the `redis.url` setting in your `config.yml` file to
371
+ connect to a Redis server. Without this setup, embeddings will only be stored
372
+ in process memory, which is less durable.
373
+
374
+ Some settings can be passed as arguments as well, e. g. if you want to choose a
375
+ specific system prompt:
376
+
377
+ ```
378
+ $ ollama_chat -s sherlock.txt
379
+ Model with architecture llama found.
380
+ Connecting to llama3.1@http://ollama.local.net:11434 now…
381
+ Configured system prompt is:
382
+ You are Sherlock Holmes and the user is your new client, Dr. Watson is also in
383
+ the room. You will talk and act in the typical manner of Sherlock Holmes do and
384
+ try to solve the user's case using logic and deduction.
385
+
386
+ Type /help to display the chat help.
387
+ 📨 user:
388
+ Good morning.
389
+ 📨 assistant:
390
+ Ah, good morning, my dear fellow! It is a pleasure to make your acquaintance. I
391
+ am Sherlock Holmes, the renowned detective, and this is my trusty sidekick, Dr.
392
+ Watson. Please, have a seat and tell us about the nature of your visit. What
393
+ seems to be the problem that has brought you to our humble abode at 221B Baker
394
+ Street?
395
+
396
+ (Watson nods in encouragement as he takes notes)
397
+
398
+ Now, pray tell, what is it that puzzles you, my dear client? A missing item,
399
+ perhaps? Or a mysterious occurrence that requires clarification? The game, as
400
+ they say, is afoot!
401
+ ```
402
+
403
+ This example shows how an image like this can be sent to a vision model for
404
+ analysis:
405
+
406
+ ![cat](spec/assets/kitten.jpg)
407
+
408
+ ```
409
+ $ ollama_chat -m llava-llama3
410
+ Model with architecture llama found.
411
+ Connecting to llava-llama3@http://localhost:11434 now…
412
+ Type /help to display the chat help.
413
+ 📸 user> What's on this image? ./spec/assets/kitten.jpg
414
+ 📨 assistant:
415
+ The image captures a moment of tranquility featuring a young cat. The cat,
416
+ adorned with gray and white fur marked by black stripes on its face and legs,
417
+ is the central figure in this scene. Its eyes, a striking shade of blue, are
418
+ wide open and directed towards the camera, giving an impression of curiosity or
419
+ alertness.
420
+
421
+ The cat is comfortably nestled on a red blanket, which contrasts vividly with
422
+ its fur. The blanket, soft and inviting, provides a sense of warmth to the
423
+ image. In the background, partially obscured by the cat's head, is another
424
+ blanket of similar red hue. The repetition of the color adds a sense of harmony
425
+ to the composition.
426
+
427
+ The cat's position on the right side of the photo creates an interesting
428
+ asymmetry with the camera lens, which occupies the left side of the frame. This
429
+ visual balance enhances the overall composition of the image.
430
+
431
+ There are no discernible texts or other objects in the image. The focus is
432
+ solely on the cat and its immediate surroundings. The image does not provide
433
+ any information about the location or setting beyond what has been described.
434
+ The simplicity of the scene allows the viewer to concentrate on the main
435
+ subject - the young, blue-eyed cat.
436
+ ```
437
+
438
+ The following commands can be given inside the chat, if prefixed by a `/`:
439
+
440
+ ```
441
+ /copy to copy last response to clipboard
442
+ /paste to paste content
443
+ /markdown toggle markdown output
444
+ /stream toggle stream output
445
+ /location toggle location submission
446
+ /voice( change) toggle voice output or change the voice
447
+ /list [n] list the last n / all conversation exchanges
448
+ /clear clear the whole conversation
449
+ /clobber clear the conversation and collection
450
+ /pop [n] pop the last n exchanges, defaults to 1
451
+ /model change the model
452
+ /system change system prompt (clears conversation)
453
+ /regenerate the last answer message
454
+ /collection( clear|change) change (default) collection or clear
455
+ /info show information for current session
456
+ /document_policy pick a scan policy for document references
457
+ /import source import the source's content
458
+ /summarize [n] source summarize the source's content in n words
459
+ /embedding toggle embedding paused or not
460
+ /embed source embed the source's content
461
+ /web [n] query query web search & return n or 1 results
462
+ /save filename store conversation messages
463
+ /load filename load conversation messages
464
+ /quit to quit
465
+ /help to view this help
466
+ ```
467
+
466
468
  ## Download
467
469
 
468
470
  The homepage of this library is located at
469
471
 
470
- * https://github.com/flori/ollama
472
+ * https://github.com/flori/ollama-ruby
471
473
 
472
474
  ## Author
473
475
 
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:
@@ -479,59 +480,6 @@ def parse_source(source_io)
479
480
  end
480
481
  end
481
482
 
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
483
  def http_options(url)
536
484
  options = {}
537
485
  if ssl_no_verify = $config.ssl_no_verify?
@@ -573,30 +521,90 @@ rescue => e
573
521
  STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e}\n#{e.backtrace * ?\n}"
574
522
  end
575
523
 
524
+ def add_image(images, source_io, source)
525
+ STDERR.puts "Adding #{source_io&.content_type} image #{source.to_s.inspect}."
526
+ image = Image.for_io(source_io, path: source.to_s)
527
+ (images << image).uniq!
528
+ end
529
+
530
+ def import_source(source_io, source)
531
+ source = source.to_s
532
+ puts "Importing #{italic { source_io&.content_type }} document #{source.inspect} now."
533
+ "Imported #{source.inspect}:\n%s\n\n" % parse_source(source_io)
534
+ end
535
+
576
536
  def import(source)
577
- puts "Now importing #{source.to_s.inspect}."
578
537
  fetch_source(source) do |source_io|
579
- content = parse_source(source_io)
580
- content.present? or return
538
+ content = import_source(source_io, source) or return
581
539
  source_io.rewind
582
540
  content
583
541
  end
584
542
  end
585
543
 
586
- def summarize(source, words: nil)
544
+ def summarize_source(source_io, source, words: nil)
545
+ puts "Summarizing #{italic { source_io&.content_type }} document #{source.inspect} now."
587
546
  words = words.to_i
588
547
  words < 1 and words = 100
589
- puts "Now summarizing #{source.to_s.inspect}."
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
548
+ source_content = parse_source(source_io)
549
+ source_content.present? or return
597
550
  $config.prompts.summarize % { source_content:, words: }
598
551
  end
599
552
 
553
+ def summarize(source, words: nil)
554
+ fetch_source(source) do |source_io|
555
+ content = summarize_source(source_io, source, words:) or return
556
+ source_io.rewind
557
+ content
558
+ end
559
+ end
560
+
561
+ def embed_source(source_io, source, count: nil)
562
+ $embedding.on? or return parse_source(source_io)
563
+ m = "Embedding #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
564
+ if count
565
+ puts '%u. %s' % [ count, m ]
566
+ else
567
+ puts m
568
+ end
569
+ text = parse_source(source_io) or return
570
+ text.downcase!
571
+ splitter_config = $config.embedding.splitter
572
+ inputs = nil
573
+ case splitter_config.name
574
+ when 'Character'
575
+ splitter = Ollama::Documents::Splitters::Character.new(
576
+ chunk_size: splitter_config.chunk_size,
577
+ )
578
+ inputs = splitter.split(text)
579
+ when 'RecursiveCharacter'
580
+ splitter = Ollama::Documents::Splitters::RecursiveCharacter.new(
581
+ chunk_size: splitter_config.chunk_size,
582
+ )
583
+ inputs = splitter.split(text)
584
+ when 'Semantic'
585
+ splitter = Ollama::Documents::Splitters::Semantic.new(
586
+ ollama:, model: $config.embedding.model.name,
587
+ chunk_size: splitter_config.chunk_size,
588
+ )
589
+ inputs = splitter.split(
590
+ text,
591
+ breakpoint: splitter_config.breakpoint.to_sym,
592
+ percentage: splitter_config.percentage?,
593
+ percentile: splitter_config.percentile?,
594
+ )
595
+ inputs = splitter.split(text)
596
+ end
597
+ inputs or return
598
+ source = source.to_s
599
+ if source.start_with?(?!)
600
+ source = Ollama::Utils::Width.truncate(
601
+ source[1..-1].gsub(/\W+/, ?_),
602
+ length: 10
603
+ )
604
+ end
605
+ $documents.add(inputs, source:, batch_size: $config.embedding.batch_size?)
606
+ end
607
+
600
608
  def embed(source)
601
609
  if $embedding.on?
602
610
  puts "Now embedding #{source.to_s.inspect}."
@@ -618,6 +626,7 @@ def parse_content(content, images)
618
626
  images.clear
619
627
  tags = Utils::Tags.new
620
628
 
629
+ contents = [ content ]
621
630
  content.scan(%r((?:\.\.|[.~])?/\S+|https?://\S+|#\S+)).each do |source|
622
631
  case source
623
632
  when /\A#(\S+)/
@@ -628,8 +637,15 @@ def parse_content(content, images)
628
637
  case source_io&.content_type&.media_type
629
638
  when 'image'
630
639
  add_image(images, source_io, source)
631
- when 'text', 'application'
632
- embed_source(source_io, source)
640
+ when 'text', 'application', nil
641
+ case $document_policy
642
+ when 'importing'
643
+ contents << import_source(source_io, source)
644
+ when 'embedding'
645
+ embed_source(source_io, source)
646
+ when 'summarizing'
647
+ contents << summarize_source(source_io, source)
648
+ end
633
649
  else
634
650
  STDERR.puts(
635
651
  "Cannot fetch #{source.to_s.inspect} with content type "\
@@ -639,8 +655,8 @@ def parse_content(content, images)
639
655
  end
640
656
  end
641
657
  end
642
-
643
- return content, (tags unless tags.empty?)
658
+ new_content = contents.select(&:present?).compact * "\n\n"
659
+ return new_content, (tags unless tags.empty?)
644
660
  end
645
661
 
646
662
  def choose_model(cli_model, current_model)
@@ -674,7 +690,29 @@ def choose_collection(current_collection)
674
690
  end
675
691
  ensure
676
692
  puts "Using collection #{bold{$documents.collection}}."
677
- collection_stats
693
+ info
694
+ end
695
+
696
+ def choose_document_policy
697
+ policies = %w[ importing embedding summarizing ].sort
698
+ current = if policies.index($document_policy)
699
+ $document_policy
700
+ elsif policies.index($config.document_policy)
701
+ $config.document_policy
702
+ else
703
+ policies.first
704
+ end
705
+ policies.unshift('[EXIT]')
706
+ policy = Ollama::Utils::Chooser.choose(policies)
707
+ case policy
708
+ when nil, '[EXIT]'
709
+ puts "Exiting chooser."
710
+ policy = current
711
+ end
712
+ $document_policy = policy
713
+ ensure
714
+ puts "Using document policy #{bold{$document_policy}}."
715
+ info
678
716
  end
679
717
 
680
718
  def collection_stats
@@ -756,6 +794,7 @@ def info
756
794
  $markdown.show
757
795
  $stream.show
758
796
  $location.show
797
+ puts "Document policy for references in user text: #{bold{$document_policy}}"
759
798
  if $voice.on?
760
799
  puts "Using voice #{bold{$current_voice}} to speak."
761
800
  end
@@ -799,6 +838,7 @@ def display_chat_help
799
838
  /regenerate the last answer message
800
839
  /collection( clear|change) change (default) collection or clear
801
840
  /info show information for current session
841
+ /document_policy pick a scan policy for document references
802
842
  /import source import the source's content
803
843
  /summarize [n] source summarize the source's content in n words
804
844
  /embedding toggle embedding paused or not
@@ -853,10 +893,11 @@ $opts[?V] and version
853
893
  base_url = $opts[?u] || $config.url
854
894
  $ollama = Client.new(base_url:, debug: $config.debug)
855
895
 
856
- $model = choose_model($opts[?m], $config.model.name)
857
- options = Options[$config.model.options]
858
- model_system = pull_model_unless_present($model, options)
859
- messages = []
896
+ $document_policy = $config.document_policy
897
+ $model = choose_model($opts[?m], $config.model.name)
898
+ options = Options[$config.model.options]
899
+ model_system = pull_model_unless_present($model, options)
900
+ messages = []
860
901
  $embedding_enabled.set($config.embedding.enabled && !$opts[?E])
861
902
 
862
903
  if $opts[?c]
@@ -969,7 +1010,7 @@ loop do
969
1010
  puts "Cleared messages."
970
1011
  next
971
1012
  when %r(^/clobber$)
972
- if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
1013
+ if ask?(prompt: 'Are you sure to clear messages and collection? (y/n) ') =~ /\Ay/i
973
1014
  clear_messages(messages)
974
1015
  $documents.clear
975
1016
  puts "Cleared messages and collection #{bold{$documents.collection}}."
@@ -1034,9 +1075,12 @@ loop do
1034
1075
  choose_collection($documents.collection)
1035
1076
  end
1036
1077
  next
1037
- when %r(/info)
1078
+ when %r(^/info$)
1038
1079
  info
1039
1080
  next
1081
+ when %r(^/document_policy$)
1082
+ choose_document_policy
1083
+ next
1040
1084
  when %r(^/import\s+(.+))
1041
1085
  parse_content = false
1042
1086
  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
@@ -1,6 +1,6 @@
1
1
  module Ollama
2
2
  # Ollama version
3
- VERSION = '0.8.0'
3
+ VERSION = '0.9.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/ollama-ruby.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama-ruby 0.8.0 ruby lib
2
+ # stub: ollama-ruby 0.9.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama-ruby".freeze
6
- s.version = "0.8.0".freeze
6
+ s.version = "0.9.0".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Florian Frank".freeze]
11
- s.date = "2024-10-06"
11
+ s.date = "2024-10-18"
12
12
  s.description = "Library that allows interacting with the Ollama API".freeze
13
13
  s.email = "flori@ping.de".freeze
14
14
  s.executables = ["ollama_console".freeze, "ollama_chat".freeze, "ollama_update".freeze, "ollama_cli".freeze]
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
 
25
25
  s.specification_version = 4
26
26
 
27
- s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 1.18.0".freeze])
27
+ s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 1.19".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<webmock>.freeze, [">= 0".freeze])
@@ -54,21 +54,21 @@ RSpec.describe Ollama::Client do
54
54
  end
55
55
 
56
56
  it 'can raise error on connection error' do
57
- allow(excon).to receive(:post).and_raise Excon::Error::Socket
57
+ expect(excon).to receive(:post).and_raise Excon::Error::Socket
58
58
  expect {
59
59
  ollama.generate(model: 'llama3.1', prompt: 'Hello World')
60
60
  }.to raise_error(Ollama::Errors::SocketError)
61
61
  end
62
62
 
63
63
  it 'can raise error on timeout' do
64
- allow(excon).to receive(:post).and_raise Excon::Errors::Timeout
64
+ expect(excon).to receive(:post).and_raise Excon::Errors::Timeout
65
65
  expect {
66
66
  ollama.generate(model: 'llama3.1', prompt: 'Hello World')
67
67
  }.to raise_error(Ollama::Errors::TimeoutError)
68
68
  end
69
69
 
70
70
  it 'can raise a generic error' do
71
- allow(excon).to receive(:post).and_raise Excon::Errors::Error
71
+ expect(excon).to receive(:post).and_raise Excon::Errors::Error
72
72
  expect {
73
73
  ollama.generate(model: 'llama3.1', prompt: 'Hello World')
74
74
  }.to raise_error(Ollama::Errors::Error)
@@ -78,7 +78,7 @@ RSpec.describe Ollama::Documents::RedisBackedMemoryCache do
78
78
  end
79
79
 
80
80
  it 'returns size' do
81
- allow(cache).to receive(:count).and_return 3
81
+ expect(cache).to receive(:count).and_return 3
82
82
  expect(cache.size).to eq 3
83
83
  end
84
84
 
@@ -57,7 +57,7 @@ RSpec.describe Ollama::Documents::RedisCache do
57
57
  key, value = 'foo', { test: true }
58
58
  expect(redis).to receive(:set).with('test-' + key, JSON(value), ex: 3_600)
59
59
  cache[key] = value
60
- allow(redis).to receive(:ttl).with('test-' + key).and_return 3_600
60
+ expect(redis).to receive(:ttl).with('test-' + key).and_return 3_600
61
61
  expect(cache.ttl(key)).to eq 3_600
62
62
  end
63
63
 
@@ -42,7 +42,7 @@ RSpec.describe Ollama::Documents do
42
42
  end
43
43
 
44
44
  it 'can find strings' do
45
- allow(ollama).to receive(:embed).
45
+ expect(ollama).to receive(:embed).
46
46
  with(model:, input: [ 'foo' ], options: nil).
47
47
  and_return(double(embeddings: [ [ 0.1 ] ]))
48
48
  expect(documents << 'foo').to eq documents
@@ -57,7 +57,7 @@ RSpec.describe Ollama::Documents do
57
57
  end
58
58
 
59
59
  it 'can find only tagged strings' do
60
- allow(ollama).to receive(:embed).
60
+ expect(ollama).to receive(:embed).
61
61
  with(model:, input: [ 'foo' ], options: nil).
62
62
  and_return(double(embeddings: [ [ 0.1 ] ]))
63
63
  expect(documents.add('foo', tags: %i[ test ])).to eq documents
@@ -77,10 +77,10 @@ RSpec.describe Ollama::Documents do
77
77
  end
78
78
 
79
79
  it 'can find strings conditionally' do
80
- allow(ollama).to receive(:embed).
80
+ expect(ollama).to receive(:embed).
81
81
  with(model:, input: [ 'foobar' ], options: nil).
82
82
  and_return(double(embeddings: [ [ 0.01 ] ]))
83
- allow(ollama).to receive(:embed).
83
+ expect(ollama).to receive(:embed).
84
84
  with(model:, input: [ 'foo' ], options: nil).
85
85
  and_return(double(embeddings: [ [ 0.1 ] ]))
86
86
  expect(documents << 'foobar').to eq documents
@@ -132,7 +132,7 @@ RSpec.describe Ollama::Documents do
132
132
  end
133
133
 
134
134
  it 'can clear texts with tags' do
135
- allow(ollama).to receive(:embed).
135
+ expect(ollama).to receive(:embed).
136
136
  with(model:, input: %w[ bar ], options: nil).
137
137
  and_return(double(embeddings: [ [ 0.1 ] ]))
138
138
  expect(documents.add('foo', tags: %i[ test ])).to eq documents
@@ -25,7 +25,6 @@ RSpec.describe Ollama::Handlers::Markdown do
25
25
  it 'can markdown response as markdown' do
26
26
  output = double('output', :sync= => true)
27
27
  expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
28
- expect(output).to receive(:puts)
29
28
  markdown = described_class.new(output:)
30
29
  response = double('response', response: md, done: false)
31
30
  markdown.call(response)
@@ -36,7 +35,6 @@ RSpec.describe Ollama::Handlers::Markdown do
36
35
  it 'can markdown message content as markdown' do
37
36
  output = double('output', :sync= => true)
38
37
  expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
39
- expect(output).to receive(:puts)
40
38
  markdown = described_class.new(output:)
41
39
  response = double('response', response: nil, message: double(content: md), done: false)
42
40
  markdown.call(response)
@@ -105,7 +105,7 @@ RSpec.describe Ollama::Utils::Fetcher do
105
105
  end
106
106
 
107
107
  it 'can .execute and fail' do
108
- allow(IO).to receive(:popen).and_raise StandardError
108
+ expect(IO).to receive(:popen).and_raise StandardError
109
109
  described_class.execute('foobar') do |file|
110
110
  expect(file).to be_a StringIO
111
111
  expect(file.read).to be_empty
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.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-06 00:00:00.000000000 Z
11
+ date: 2024-10-18 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.18.0
19
+ version: '1.19'
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.18.0
26
+ version: '1.19'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: all_images
29
29
  requirement: !ruby/object:Gem::Requirement