ollama-ruby 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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