ollama-ruby 0.1.0 → 0.3.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 +105 -0
- data/README.md +14 -14
- data/Rakefile +3 -1
- data/bin/ollama_chat +181 -71
- data/bin/ollama_cli +68 -0
- data/bin/ollama_console +7 -2
- data/lib/ollama/documents/memory_cache.rb +4 -2
- data/lib/ollama/documents/redis_cache.rb +4 -3
- data/lib/ollama/documents.rb +30 -5
- data/lib/ollama/dto.rb +4 -7
- data/lib/ollama/options.rb +4 -0
- data/lib/ollama/utils/file_argument.rb +16 -0
- data/lib/ollama/utils/tags.rb +4 -0
- data/lib/ollama/utils/width.rb +14 -1
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama.rb +1 -0
- data/ollama-ruby.gemspec +8 -7
- data/spec/ollama/client_spec.rb +2 -2
- data/spec/ollama/commands/chat_spec.rb +2 -2
- data/spec/ollama/commands/copy_spec.rb +2 -2
- data/spec/ollama/commands/create_spec.rb +2 -2
- data/spec/ollama/commands/delete_spec.rb +2 -2
- data/spec/ollama/commands/embed_spec.rb +3 -3
- data/spec/ollama/commands/embeddings_spec.rb +2 -2
- data/spec/ollama/commands/generate_spec.rb +2 -2
- data/spec/ollama/commands/pull_spec.rb +2 -2
- data/spec/ollama/commands/push_spec.rb +2 -2
- data/spec/ollama/commands/show_spec.rb +2 -2
- data/spec/ollama/documents/redis_cache_spec.rb +8 -0
- data/spec/ollama/documents_spec.rb +42 -0
- data/spec/ollama/message_spec.rb +3 -4
- data/spec/ollama/options_spec.rb +18 -0
- data/spec/ollama/tool_spec.rb +1 -6
- data/tmp/.keep +0 -0
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 510c31683b2251118a7c3b469620400016b29fd1ea6f17cc86e4f99f62ecea2f
|
4
|
+
data.tar.gz: a00c4275e42002a01a99f874a855386c83e502551360c1f4146eee0b74f2fd08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17e50d40e4c24b56b5c2923f0b73f3e4294587b61a302fbaf4cd8f891e879eb435541e1f7e195a4f88352fd99aa80ff44c8577b8c195d7ef31af1c33229018b7
|
7
|
+
data.tar.gz: a18bcef82e9481b75fee4a4c88598a7a374c48f841e9870b39174c0d1f5f235f1173bdbff939182481d431e4e9c8c85501be4f5ef917ca02865692cfc39fde5a
|
data/CHANGES.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Changes
|
2
|
+
|
3
|
+
## 2024-09-05 v0.3.0
|
4
|
+
|
5
|
+
* **New Features**
|
6
|
+
* Created new file `ollama_cli` with Ollama CLI functionality.
|
7
|
+
* Added executable `ollama_cli` to s.executables in ollama-ruby.gemspec.
|
8
|
+
* Added `find_where` method in `documents.rb` to filter records by text size
|
9
|
+
and count.
|
10
|
+
* Added test for `find_where` method in `documents_spec.rb`.
|
11
|
+
* Features for `ollama_chat`
|
12
|
+
* Added `found_texts_count` option to `OllamaChatConfig`.
|
13
|
+
* Implemented `parse_rss` method for RSS feeds and `parse_atom` method
|
14
|
+
for Atom feeds.
|
15
|
+
* Added links to titles in RSS feed item summaries and Atom feed item
|
16
|
+
summaries.
|
17
|
+
* Updated `parse_source` method to handle different content types,
|
18
|
+
including HTML, XML, and RSS/Atom feeds.
|
19
|
+
* Added `/web [n] query` command to search web and return n or 1 results
|
20
|
+
in chat interface.
|
21
|
+
* **Improvements**
|
22
|
+
* Improved validation for system prompts
|
23
|
+
* Extracted file argument handling into a separate module and method
|
24
|
+
* Added default value for config or model system prompt
|
25
|
+
* Improved input validation for `system_prompt` path
|
26
|
+
* Updated collection clearing logic to accept optional tags parameter
|
27
|
+
* Updated `Tags` class to overload `to_a` method for converting to array of
|
28
|
+
strings
|
29
|
+
|
30
|
+
## 2024-09-03 v0.2.0
|
31
|
+
|
32
|
+
### Changes
|
33
|
+
|
34
|
+
* **Added Web Search Functionality to `ollama_chat`**
|
35
|
+
+ Added `/web` command to fetch search results from DuckDuckGo
|
36
|
+
+ Updated `/summarize` command to handle cases where summarization fails
|
37
|
+
+ Fix bug in parsing content type of source document
|
38
|
+
* **Refactored Options Class and Usage**
|
39
|
+
+ Renamed `options` variable to use `Options[]` method in ollama_chat script
|
40
|
+
+ Added `[](value)` method to Ollama::Options class for casting hashes
|
41
|
+
+ Updated options_spec.rb with tests for casting hashes and error handling
|
42
|
+
* **Refactored Web Search Command**
|
43
|
+
+ Added support for specifying a page number in `/web` command
|
44
|
+
+ Updated regular expression to match new format
|
45
|
+
+ Passed page number as an argument to `search_web` method
|
46
|
+
+ Updated content string to reference the query and sources correctly
|
47
|
+
* **DTO Class Changes**
|
48
|
+
+ Renamed `json_create` method to `from_hash` in Ollama::DTO class
|
49
|
+
+ Updated `as_json` method to remove now unnecessary hash creation
|
50
|
+
* **Message and Tool Spec Changes**
|
51
|
+
+ Removed `json_class` from JSON serialization in message_spec
|
52
|
+
+ Removed `json_class` from JSON serialization in tool_spec
|
53
|
+
* **Command Spec Changes**
|
54
|
+
+ Removed `json_class` from JSON serialization in various command specs (e.g. generate_spec, pull_spec, etc.)
|
55
|
+
* **Miscellaneous Changes**
|
56
|
+
+ Improved width calculation for text truncation
|
57
|
+
+ Updated FollowChat class to display evaluation statistics
|
58
|
+
+ Update OllamaChatConfig to use EOT instead of end for heredoc syntax
|
59
|
+
+ Add .keep file to tmp directory
|
60
|
+
|
61
|
+
## 2024-08-30 v0.1.0
|
62
|
+
|
63
|
+
### Change Log for New Version
|
64
|
+
|
65
|
+
#### Significant Changes
|
66
|
+
|
67
|
+
* **Document Splitting and Embedding Functionality**: Added `Ollama::Documents` class with methods for adding documents, checking existence, deleting documents, and finding similar documents.
|
68
|
+
+ Introduced two types of caches: `MemoryCache` and `RedisCache`
|
69
|
+
+ Implemented `SemanticSplitter` class to split text into sentences based on semantic similarity
|
70
|
+
* **Improved Ollama Chat Client**: Added support for document embeddings and web/file RAG
|
71
|
+
+ Allowed configuration per yaml file
|
72
|
+
+ Parse user input for URLs or files to send images to multimodal models
|
73
|
+
* **Redis Docker Service**: Set `REDIS_URL` environment variable to `redis://localhost:9736`
|
74
|
+
+ Added Redis service to `docker-compose.yml`
|
75
|
+
* **Status Display and Progress Updates**: Added infobar.label = response.status when available
|
76
|
+
+ Updated infobar with progress message on each call if total and completed are set
|
77
|
+
+ Display error message from response.error if present
|
78
|
+
* **Refactored Chat Commands**: Simplified regular expression patterns for `/pop`, `/save`, `/load`, and `/image` commands
|
79
|
+
+ Added whitespace to some command patterns for better readability
|
80
|
+
|
81
|
+
#### Other Changes
|
82
|
+
|
83
|
+
* Added `Character` and `RecursiveCharacter` splitter classes to split text into chunks based on character separators
|
84
|
+
* Added RSpec tests for the Ollama::Documents class(es)
|
85
|
+
* Updated dependencies and added new methods for calculating breakpoint thresholds and sentence embeddings
|
86
|
+
* Added 'ollama_update' to executables in Rakefile
|
87
|
+
* Started using webmock
|
88
|
+
* Refactored chooser and add fetcher specs
|
89
|
+
* Added tests for Ollama::Utils::Fetcher
|
90
|
+
* Update README.md
|
91
|
+
|
92
|
+
## 2024-08-16 v0.0.1
|
93
|
+
|
94
|
+
* **New Features**
|
95
|
+
+ Added missing options parameter to Embed command
|
96
|
+
+ Documented new `/api/embed` endpoint
|
97
|
+
* **Improvements**
|
98
|
+
+ Improved example in README.md
|
99
|
+
* **Code Refactoring**
|
100
|
+
+ Renamed `client` to `ollama` in client and command specs
|
101
|
+
+ Updated expectations to use `ollama` instead of `client`
|
102
|
+
|
103
|
+
## 2024-08-12 v0.0.0
|
104
|
+
|
105
|
+
* Start
|
data/README.md
CHANGED
@@ -43,7 +43,6 @@ ollama_chat [OPTIONS]
|
|
43
43
|
-c CHAT a saved chat conversation to load
|
44
44
|
-C COLLECTION name of the collection used in this conversation
|
45
45
|
-D DOCUMENT load document and add to collection (multiple)
|
46
|
-
-d use markdown to display the chat messages
|
47
46
|
-v use voice output
|
48
47
|
-h this help
|
49
48
|
```
|
@@ -153,19 +152,20 @@ subject - the young, blue-eyed cat.
|
|
153
152
|
The following commands can be given inside the chat, if prefixed by a `/`:
|
154
153
|
|
155
154
|
```
|
156
|
-
/paste
|
157
|
-
/markdown
|
158
|
-
/list
|
159
|
-
/clear
|
160
|
-
/pop [n]
|
161
|
-
/model
|
162
|
-
/regenerate
|
163
|
-
/collection clear|stats|change|new clear or show stats of current collection
|
164
|
-
/summarize source
|
165
|
-
/
|
166
|
-
/
|
167
|
-
/
|
168
|
-
/
|
155
|
+
/paste to paste content
|
156
|
+
/markdown toggle markdown output
|
157
|
+
/list list the messages of the conversation
|
158
|
+
/clear clear the conversation messages
|
159
|
+
/pop [n] pop the last n exchanges, defaults to 1
|
160
|
+
/model change the model
|
161
|
+
/regenerate the last answer message
|
162
|
+
/collection clear [tag]|stats|change|new clear or show stats of current collection
|
163
|
+
/summarize source summarize the URL/file source's content
|
164
|
+
/web [n] query query web search & return n or 1 results
|
165
|
+
/save filename store conversation messages
|
166
|
+
/load filename load conversation messages
|
167
|
+
/quit to quit
|
168
|
+
/help to view this help
|
169
169
|
```
|
170
170
|
|
171
171
|
### ollama\_console
|
data/Rakefile
CHANGED
@@ -18,7 +18,8 @@ GemHadar do
|
|
18
18
|
'.utilsrc', '.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
|
19
19
|
readme 'README.md'
|
20
20
|
|
21
|
-
executables << 'ollama_console' << 'ollama_chat' <<
|
21
|
+
executables << 'ollama_console' << 'ollama_chat' <<
|
22
|
+
'ollama_update' << 'ollama_cli'
|
22
23
|
|
23
24
|
required_ruby_version '~> 3.1'
|
24
25
|
|
@@ -36,6 +37,7 @@ GemHadar do
|
|
36
37
|
dependency 'complex_config', '~> 0.20'
|
37
38
|
dependency 'search_ui', '~> 0.0'
|
38
39
|
dependency 'amatch', '~> 0.4.1'
|
40
|
+
dependency 'pdf-reader', '~> 2.0'
|
39
41
|
development_dependency 'all_images', '~> 0.4'
|
40
42
|
development_dependency 'rspec', '~> 3.2'
|
41
43
|
development_dependency 'utils'
|
data/bin/ollama_chat
CHANGED
@@ -4,18 +4,22 @@ require 'ollama'
|
|
4
4
|
include Ollama
|
5
5
|
require 'term/ansicolor'
|
6
6
|
include Term::ANSIColor
|
7
|
-
require 'tins
|
7
|
+
require 'tins'
|
8
8
|
include Tins::GO
|
9
9
|
require 'reline'
|
10
10
|
require 'reverse_markdown'
|
11
11
|
require 'complex_config'
|
12
12
|
require 'fileutils'
|
13
|
+
require 'uri'
|
14
|
+
require 'nokogiri'
|
15
|
+
require 'rss'
|
16
|
+
require 'pdf/reader'
|
13
17
|
|
14
18
|
class OllamaChatConfig
|
15
19
|
include ComplexConfig
|
16
20
|
include FileUtils
|
17
21
|
|
18
|
-
DEFAULT_CONFIG = <<~
|
22
|
+
DEFAULT_CONFIG = <<~EOT
|
19
23
|
---
|
20
24
|
url: <%= ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST') %>
|
21
25
|
model:
|
@@ -34,6 +38,7 @@ class OllamaChatConfig
|
|
34
38
|
prompt: 'Represent this sentence for searching relevant passages: %s'
|
35
39
|
collection: <%= ENV.fetch('OLLAMA_CHAT_COLLECTION', 'ollama_chat') %>
|
36
40
|
found_texts_size: 4096
|
41
|
+
found_texts_count: null
|
37
42
|
splitter:
|
38
43
|
name: RecursiveCharacter
|
39
44
|
chunk_size: 1024
|
@@ -41,7 +46,7 @@ class OllamaChatConfig
|
|
41
46
|
redis:
|
42
47
|
url: <%= ENV.fetch('REDIS_URL', 'null') %>
|
43
48
|
debug: <%= ENV['OLLAMA_CHAT_DEBUG'].to_i == 1 ? true : false %>
|
44
|
-
|
49
|
+
EOT
|
45
50
|
|
46
51
|
def initialize(filename = nil)
|
47
52
|
@filename = filename || default_path
|
@@ -109,14 +114,50 @@ class FollowChat
|
|
109
114
|
end
|
110
115
|
@say.call(response)
|
111
116
|
end
|
112
|
-
response.done
|
117
|
+
if response.done
|
118
|
+
@output.puts
|
119
|
+
eval_stats = {
|
120
|
+
eval_duration: Tins::Duration.new(response.eval_duration / 1e9),
|
121
|
+
eval_count: response.eval_count,
|
122
|
+
prompt_eval_duration: Tins::Duration.new(response.prompt_eval_duration / 1e9),
|
123
|
+
prompt_eval_count: response.prompt_eval_count,
|
124
|
+
total_duration: Tins::Duration.new(response.total_duration / 1e9),
|
125
|
+
load_duration: Tins::Duration.new(response.load_duration / 1e9),
|
126
|
+
}.map { _1 * '=' } * ' '
|
127
|
+
@output.puts '📊 ' + color(111) { Utils::Width.wrap(eval_stats, percentage: 90) }
|
128
|
+
end
|
113
129
|
self
|
114
130
|
end
|
115
131
|
end
|
116
132
|
|
133
|
+
def search_web(query, n = 5)
|
134
|
+
query = URI.encode_uri_component(query)
|
135
|
+
url = "https://www.duckduckgo.com/html/?q=#{query}"
|
136
|
+
Ollama::Utils::Fetcher.new.get(url) do |tmp|
|
137
|
+
result = []
|
138
|
+
doc = Nokogiri::HTML(tmp)
|
139
|
+
doc.css('.results_links').each do |link|
|
140
|
+
if n > 0
|
141
|
+
url = link.css('.result__a').first&.[]('href')
|
142
|
+
url.sub!(%r(\A/l/\?uddg=), '')
|
143
|
+
url.sub!(%r(&rut=.*), '')
|
144
|
+
url = URI.decode_uri_component(url)
|
145
|
+
url = URI.parse(url)
|
146
|
+
url.host =~ /duckduckgo\.com/ and next
|
147
|
+
result << url
|
148
|
+
n -= 1
|
149
|
+
else
|
150
|
+
break
|
151
|
+
end
|
152
|
+
end
|
153
|
+
result
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
117
157
|
def pull_model_unless_present(model, options, retried = false)
|
118
158
|
ollama.show(name: model) { |response|
|
119
|
-
puts "Model #{bold{model}} with architecture
|
159
|
+
puts "Model #{bold{model}} with architecture "\
|
160
|
+
"#{response.model_info['general.architecture']} found."
|
120
161
|
if system = response.system
|
121
162
|
puts "Configured model system prompt is:\n#{italic { system }}"
|
122
163
|
return system
|
@@ -144,7 +185,7 @@ def load_conversation(filename)
|
|
144
185
|
return
|
145
186
|
end
|
146
187
|
File.open(filename, 'r') do |output|
|
147
|
-
return JSON(output.read
|
188
|
+
return JSON(output.read).map { Ollama::Message.from_hash(_1) }
|
148
189
|
end
|
149
190
|
end
|
150
191
|
|
@@ -189,19 +230,79 @@ def list_conversation(messages, markdown)
|
|
189
230
|
end
|
190
231
|
end
|
191
232
|
|
233
|
+
def reverse_markdown(html)
|
234
|
+
ReverseMarkdown.convert(
|
235
|
+
html,
|
236
|
+
unknown_tags: :bypass,
|
237
|
+
github_flavored: true,
|
238
|
+
tag_border: ''
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
242
|
+
def parse_rss(source_io)
|
243
|
+
feed = RSS::Parser.parse(source_io, false, false)
|
244
|
+
title = <<~end
|
245
|
+
# #{feed&.channel&.title}
|
246
|
+
|
247
|
+
end
|
248
|
+
feed.items.inject(title) do |text, item|
|
249
|
+
text << <<~end
|
250
|
+
## [#{item&.title}](#{item&.link})
|
251
|
+
|
252
|
+
updated on #{item&.pubDate}
|
253
|
+
|
254
|
+
#{reverse_markdown(item&.description)}
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def parse_atom(source_io)
|
261
|
+
feed = RSS::Parser.parse(source_io, false, false)
|
262
|
+
title = <<~end
|
263
|
+
# #{feed.title.content}
|
264
|
+
|
265
|
+
end
|
266
|
+
feed.items.inject(title) do |text, item|
|
267
|
+
text << <<~end
|
268
|
+
## [#{item&.title&.content}](#{item&.link&.href})
|
269
|
+
|
270
|
+
updated on #{item&.updated&.content}
|
271
|
+
|
272
|
+
#{reverse_markdown(item&.content&.content)}
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
192
278
|
def parse_source(source_io)
|
193
|
-
case source_io&.content_type
|
194
|
-
when 'html'
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
279
|
+
case source_io&.content_type
|
280
|
+
when 'text/html'
|
281
|
+
reverse_markdown(source_io.read)
|
282
|
+
when 'text/xml'
|
283
|
+
if source_io.readline =~ %r(^\s*<rss\s)
|
284
|
+
source_io.rewind
|
285
|
+
return parse_rss(source_io)
|
286
|
+
end
|
287
|
+
source_io.rewind
|
288
|
+
source_io.read
|
289
|
+
when %r(\Atext/)
|
290
|
+
source_io.read
|
291
|
+
when 'application/rss+xml'
|
292
|
+
parse_rss(source_io)
|
293
|
+
when 'application/atom+xml'
|
294
|
+
parse_atom(source_io)
|
295
|
+
when 'application/json'
|
202
296
|
source_io.read
|
297
|
+
when 'application/pdf'
|
298
|
+
reader = PDF::Reader.new(source_io)
|
299
|
+
result = +''
|
300
|
+
reader.pages.each do |page|
|
301
|
+
result << page.text
|
302
|
+
end
|
303
|
+
result
|
203
304
|
else
|
204
|
-
STDERR.puts "Cannot import #{source_io
|
305
|
+
STDERR.puts "Cannot import #{source_io&.content_type} document."
|
205
306
|
return
|
206
307
|
end
|
207
308
|
end
|
@@ -211,7 +312,7 @@ def import_document(source_io, source)
|
|
211
312
|
STDOUT.puts "Embedding disabled, I won't import any documents, try: /summarize"
|
212
313
|
return
|
213
314
|
end
|
214
|
-
|
315
|
+
infobar.puts "Importing #{italic { source_io.content_type }} document #{source.to_s.inspect}."
|
215
316
|
text = parse_source(source_io) or return
|
216
317
|
text.downcase!
|
217
318
|
splitter_config = $config.embedding.splitter
|
@@ -290,7 +391,7 @@ def parse_content(content, images)
|
|
290
391
|
case source_io&.content_type&.media_type
|
291
392
|
when 'image'
|
292
393
|
add_image(images, source_io, source)
|
293
|
-
when 'text'
|
394
|
+
when 'text', 'application'
|
294
395
|
import_document(source_io, source)
|
295
396
|
else
|
296
397
|
STDERR.puts(
|
@@ -354,19 +455,20 @@ end
|
|
354
455
|
|
355
456
|
def display_chat_help
|
356
457
|
puts <<~end
|
357
|
-
/paste
|
358
|
-
/markdown
|
359
|
-
/list
|
360
|
-
/clear
|
361
|
-
/pop [n]
|
362
|
-
/model
|
363
|
-
/regenerate
|
364
|
-
/collection clear|stats|change|new clear or show stats of current collection
|
365
|
-
/summarize source
|
366
|
-
/
|
367
|
-
/
|
368
|
-
/
|
369
|
-
/
|
458
|
+
/paste to paste content
|
459
|
+
/markdown toggle markdown output
|
460
|
+
/list list the messages of the conversation
|
461
|
+
/clear clear the conversation messages
|
462
|
+
/pop [n] pop the last n exchanges, defaults to 1
|
463
|
+
/model change the model
|
464
|
+
/regenerate the last answer message
|
465
|
+
/collection clear [tag]|stats|change|new clear or show stats of current collection
|
466
|
+
/summarize source summarize the URL/file source's content
|
467
|
+
/web [n] query query web search & return n or 1 results
|
468
|
+
/save filename store conversation messages
|
469
|
+
/load filename load conversation messages
|
470
|
+
/quit to quit
|
471
|
+
/help to view this help
|
370
472
|
end
|
371
473
|
end
|
372
474
|
|
@@ -381,7 +483,6 @@ def usage
|
|
381
483
|
-c CHAT a saved chat conversation to load
|
382
484
|
-C COLLECTION name of the collection used in this conversation
|
383
485
|
-D DOCUMENT load document and add to collection (multiple)
|
384
|
-
-d use markdown to display the chat messages
|
385
486
|
-v use voice output
|
386
487
|
-h this help
|
387
488
|
|
@@ -393,7 +494,7 @@ def ollama
|
|
393
494
|
$ollama
|
394
495
|
end
|
395
496
|
|
396
|
-
opts = go 'f:u:m:s:c:C:D:
|
497
|
+
opts = go 'f:u:m:s:c:C:D:vh'
|
397
498
|
|
398
499
|
config = OllamaChatConfig.new(opts[?f])
|
399
500
|
$config = config.config
|
@@ -407,13 +508,13 @@ base_url = opts[?u] || $config.url
|
|
407
508
|
$ollama = Client.new(base_url:, debug: $config.debug)
|
408
509
|
|
409
510
|
model = choose_model(opts[?m], $config.model.name)
|
410
|
-
options = $config.model.options
|
511
|
+
options = Options[$config.model.options]
|
411
512
|
model_system = pull_model_unless_present(model, options)
|
412
513
|
messages = []
|
413
514
|
|
414
515
|
if $config.embedding.enabled
|
415
516
|
embedding_model = $config.embedding.model.name
|
416
|
-
embedding_model_options = $config.embedding.model.options
|
517
|
+
embedding_model_options = Options[$config.embedding.model.options]
|
417
518
|
pull_model_unless_present(embedding_model, embedding_model_options)
|
418
519
|
collection = opts[?C] || $config.embedding.collection
|
419
520
|
$documents = Documents.new(
|
@@ -456,23 +557,15 @@ end
|
|
456
557
|
if voice = ($config.voice if opts[?v])
|
457
558
|
puts "Using voice #{bold{voice}} to speak."
|
458
559
|
end
|
459
|
-
|
460
|
-
markdown = set_markdown(opts[?d] || $config.markdown)
|
560
|
+
markdown = set_markdown($config.markdown)
|
461
561
|
|
462
562
|
if opts[?c]
|
463
563
|
messages.concat load_conversation(opts[?c])
|
464
564
|
else
|
465
|
-
system =
|
466
|
-
|
467
|
-
system = File.read(system_prompt_file)
|
468
|
-
end
|
469
|
-
system ||= $config.system
|
470
|
-
|
471
|
-
if system
|
565
|
+
if system = Ollama::Utils::FileArgument.
|
566
|
+
get_file_argument(opts[?s], default: $config.system? || model_system)
|
472
567
|
messages << Message.new(role: 'system', content: system)
|
473
568
|
puts "Configured system prompt is:\n#{italic { system }}"
|
474
|
-
elsif model_system.present?
|
475
|
-
puts "Using model system prompt."
|
476
569
|
end
|
477
570
|
end
|
478
571
|
|
@@ -481,9 +574,10 @@ puts "\nType /help to display the chat help."
|
|
481
574
|
images = []
|
482
575
|
loop do
|
483
576
|
parse_content = true
|
484
|
-
|
485
577
|
input_prompt = bold { color(172) { message_type(images) + " user" } } + bold { "> " }
|
486
|
-
|
578
|
+
content = Reline.readline(input_prompt, true)&.chomp
|
579
|
+
|
580
|
+
case content
|
487
581
|
when %r(^/paste$)
|
488
582
|
puts bold { "Paste your content and then press C-d!" }
|
489
583
|
content = STDIN.read
|
@@ -500,11 +594,18 @@ loop do
|
|
500
594
|
messages.clear
|
501
595
|
puts "Cleared messages."
|
502
596
|
next
|
503
|
-
when %r(^/collection
|
504
|
-
|
597
|
+
when %r(^/collection\s+(clear|stats|change|new)(?:\s+(.+))?$)
|
598
|
+
command, arg = $1, $2
|
599
|
+
case command
|
505
600
|
when 'clear'
|
506
|
-
|
507
|
-
|
601
|
+
tags = arg.present? ? arg.sub(/\A#*/, '') : nil
|
602
|
+
if tags
|
603
|
+
$documents.clear(tags:)
|
604
|
+
puts "Cleared tag ##{tags} from collection #{bold{collection}}."
|
605
|
+
else
|
606
|
+
$documents.clear
|
607
|
+
puts "Cleared collection #{bold{collection}}."
|
608
|
+
end
|
508
609
|
when 'stats'
|
509
610
|
collection_stats
|
510
611
|
when 'change'
|
@@ -518,7 +619,7 @@ loop do
|
|
518
619
|
when %r(^/pop?(?:\s+(\d*))?$)
|
519
620
|
n = $1.to_i.clamp(1, Float::INFINITY)
|
520
621
|
r = messages.pop(2 * n)
|
521
|
-
m = r.size
|
622
|
+
m = r.size / 2
|
522
623
|
puts "Popped the last #{m} exchanges."
|
523
624
|
next
|
524
625
|
when %r(^/model$)
|
@@ -534,7 +635,15 @@ loop do
|
|
534
635
|
end
|
535
636
|
when %r(^/summarize\s+(.+))
|
536
637
|
parse_content = false
|
537
|
-
content = summarize($1)
|
638
|
+
content = summarize($1) or next
|
639
|
+
when %r(^/web\s+(?:(\d+)\s+)?(.+)$)
|
640
|
+
parse_content = true
|
641
|
+
urls = search_web($2, $1.to_i)
|
642
|
+
content = <<~end
|
643
|
+
Answer the the query #{$2.inspect} using these sources:
|
644
|
+
|
645
|
+
#{urls * ?\n}
|
646
|
+
end
|
538
647
|
when %r(^/save\s+(.+)$)
|
539
648
|
save_conversation($1, messages)
|
540
649
|
puts "Saved conversation to #$1."
|
@@ -557,19 +666,18 @@ loop do
|
|
557
666
|
[ content, Utils::Tags.new ]
|
558
667
|
end
|
559
668
|
|
560
|
-
if $config.embedding.enabled
|
561
|
-
records = $documents.
|
669
|
+
if $config.embedding.enabled && content
|
670
|
+
records = $documents.find_where(
|
562
671
|
content.downcase,
|
563
672
|
tags:,
|
564
|
-
prompt:
|
673
|
+
prompt: $config.embedding.model.prompt?,
|
674
|
+
text_size: $config.embedding.found_texts_size?,
|
675
|
+
text_count: $config.embedding.found_texts_count?,
|
565
676
|
)
|
566
|
-
s, found_texts_size = 0, $config.embedding.found_texts_size
|
567
|
-
records = records.take_while {
|
568
|
-
(s += _1.text.size) <= found_texts_size
|
569
|
-
}
|
570
677
|
found_texts = records.map(&:text)
|
571
678
|
unless found_texts.empty?
|
572
|
-
content += "\nConsider these chunks for your answer:\n
|
679
|
+
content += "\nConsider these chunks for your answer:\n"\
|
680
|
+
"#{found_texts.join("\n\n---\n\n")}"
|
573
681
|
end
|
574
682
|
end
|
575
683
|
|
@@ -577,15 +685,17 @@ loop do
|
|
577
685
|
handler = FollowChat.new(messages:, markdown:, voice:)
|
578
686
|
ollama.chat(model:, messages:, options:, stream: true, &handler)
|
579
687
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
688
|
+
if records
|
689
|
+
puts records.map { |record|
|
690
|
+
link = if record.source =~ %r(\Ahttps?://)
|
691
|
+
record.source
|
692
|
+
else
|
693
|
+
'file://%s' % File.expand_path(record.source)
|
694
|
+
end
|
695
|
+
[ link, record.tags.first ]
|
696
|
+
}.uniq.map { |l, t| hyperlink(l, t) }.join(' ')
|
697
|
+
$config.debug and jj messages
|
698
|
+
end
|
589
699
|
rescue Interrupt
|
590
700
|
puts "Type /quit to quit."
|
591
701
|
end
|
data/bin/ollama_cli
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ollama'
|
4
|
+
include Ollama
|
5
|
+
include Ollama::Utils::FileArgument
|
6
|
+
require 'tins'
|
7
|
+
include Tins::GO
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
def usage
|
11
|
+
puts <<~end
|
12
|
+
#{File.basename($0)} [OPTIONS]
|
13
|
+
|
14
|
+
-u URL the ollama base url, OLLAMA_URL
|
15
|
+
-m MODEL the ollama model to chat with, OLLAMA_MODEL
|
16
|
+
-M OPTIONS the ollama model options to use, OLLAMA_MODEL_OPTIONS
|
17
|
+
-s SYSTEM the system prompt to use as a file, OLLAMA_SYSTEM
|
18
|
+
-p PROMPT the user prompt to use as a file, OLLAMA_PROMPT
|
19
|
+
-H HANDLER the handler to use for the response, defaults to Print
|
20
|
+
-S use streaming for generation
|
21
|
+
-h this help
|
22
|
+
|
23
|
+
end
|
24
|
+
exit 0
|
25
|
+
end
|
26
|
+
|
27
|
+
opts = go 'u:m:M:s:p:H:Sh', defaults: { ?H => 'Print', ?M => '{}' }
|
28
|
+
|
29
|
+
opts[?h] and usage
|
30
|
+
|
31
|
+
base_url = opts[?u] || ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
|
32
|
+
model = opts[?m] || ENV.fetch('OLLAMA_MODEL', 'llama3.1')
|
33
|
+
options = Ollama::Options.from_hash(JSON(
|
34
|
+
get_file_argument(opts[?M], default: ENV['OLLAMA_MODEL_OPTIONS'])
|
35
|
+
))
|
36
|
+
system = get_file_argument(opts[?s], default: ENV['OLLAMA_SYSTEM'])
|
37
|
+
prompt = get_file_argument(opts[?p], default: ENV['OLLAMA_PROMPT'])
|
38
|
+
|
39
|
+
if prompt.nil?
|
40
|
+
prompt = STDIN.read
|
41
|
+
elsif c = prompt.scan('%s').size
|
42
|
+
case c
|
43
|
+
when 0
|
44
|
+
when 1
|
45
|
+
prompt = prompt % STDIN.read
|
46
|
+
else
|
47
|
+
STDERR.puts "Found more than one plaeceholder %s. => Ignoring."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if ENV['DEBUG'].to_i == 1
|
52
|
+
puts <<~EOT
|
53
|
+
base_url = #{base_url.inspect}
|
54
|
+
model = #{model.inspect}
|
55
|
+
system = #{system.inspect}
|
56
|
+
prompt = #{prompt.inspect}
|
57
|
+
options = #{options.to_json}
|
58
|
+
EOT
|
59
|
+
end
|
60
|
+
|
61
|
+
Client.new(base_url:, read_timeout: 120).generate(
|
62
|
+
model:,
|
63
|
+
system:,
|
64
|
+
prompt:,
|
65
|
+
options:,
|
66
|
+
stream: !!opts[?S],
|
67
|
+
&Object.const_get(opts[?H])
|
68
|
+
)
|
data/bin/ollama_console
CHANGED
@@ -5,8 +5,13 @@ include Ollama
|
|
5
5
|
require 'irb'
|
6
6
|
require 'irb/history'
|
7
7
|
|
8
|
-
base_url
|
9
|
-
|
8
|
+
def base_url
|
9
|
+
ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
|
10
|
+
end
|
11
|
+
|
12
|
+
def ollama
|
13
|
+
$ollama ||= Client.new(base_url:)
|
14
|
+
end
|
10
15
|
IRB.setup nil
|
11
16
|
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
|
12
17
|
IRB.conf[:HISTORY_FILE] = File.join(ENV.fetch('HOME'), '.ollama_console-history')
|