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 +4 -4
- data/CHANGES.md +18 -0
- data/README.md +168 -166
- data/bin/ollama_chat +120 -76
- data/lib/ollama/handlers/markdown.rb +1 -2
- 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 +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0b3ea2bd67e3f4fa2ca4da88a25cb869fc81e35578f1e7fd97ba43e84fc2278
|
4
|
+
data.tar.gz: 626c534bd2fd0580ab0520e8b70587489edbc963e179be0f800ee63235690b33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
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
|
-

|
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
|
-
|
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
|
-
|
33
|
+
```ruby
|
34
|
+
require "ollama"
|
35
|
+
include Ollama
|
155
36
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
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
|
+

|
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 =
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
$
|
857
|
-
|
858
|
-
|
859
|
-
|
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(
|
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
|
data/lib/ollama/version.rb
CHANGED
data/ollama-ruby.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: ollama-ruby 0.
|
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.
|
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-
|
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.
|
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])
|
data/spec/ollama/client_spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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)
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
80
|
+
expect(ollama).to receive(:embed).
|
81
81
|
with(model:, input: [ 'foobar' ], options: nil).
|
82
82
|
and_return(double(embeddings: [ [ 0.01 ] ]))
|
83
|
-
|
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
|
-
|
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
|
-
|
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.
|
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-
|
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.
|
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.
|
26
|
+
version: '1.19'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: all_images
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|