ollama-ruby 0.3.2 → 0.4.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 +28 -0
- data/README.md +2 -0
- data/Rakefile +4 -1
- data/bin/ollama_chat +63 -42
- data/lib/ollama/documents/cache/common.rb +17 -0
- data/lib/ollama/documents/{memory_cache.rb → cache/memory_cache.rb} +8 -10
- data/lib/ollama/documents/cache/redis_backed_memory_cache.rb +44 -0
- data/lib/ollama/documents/{redis_cache.rb → cache/redis_cache.rb} +9 -8
- data/lib/ollama/documents.rb +19 -16
- data/lib/ollama/utils/colorize_texts.rb +21 -1
- data/lib/ollama/utils/fetcher.rb +8 -6
- data/lib/ollama/utils/file_argument.rb +18 -0
- data/lib/ollama/utils/tags.rb +1 -0
- data/lib/ollama/version.rb +1 -1
- data/ollama-ruby.gemspec +8 -6
- data/spec/ollama/documents/memory_cache_spec.rb +16 -16
- data/spec/ollama/documents/redis_backed_memory_cache_spec.rb +95 -0
- data/spec/ollama/documents/redis_cache_spec.rb +15 -15
- data/spec/ollama/utils/fetcher_spec.rb +2 -1
- metadata +40 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29863f68627a05541de5eab810850b0509db94c1d91b2e9a5a9be9bd387e8aea
|
4
|
+
data.tar.gz: 07f3558153a0a724c08e27a86482e0951070348bf25641d2a9feed113e3e7046
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11c405277b59c6acc75c24220942dd883bb7aec99d97d19bc9baf8d61aa4e5dc59af905631dc37641b91babdd9e5ab7305720a8720451db7b4bb6ca067833913
|
7
|
+
data.tar.gz: 129f49e2d4b5cb65b7999f666f33798511b8a34b1be84bbc7bd4433872cd7c323812cd4dd3e8d2ae325cb013cfb6403db8181c8cbe641139469e5ea9821cc71d
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 2024-09-21 v0.4.0
|
4
|
+
|
5
|
+
### Change Log for **1.2.3**
|
6
|
+
|
7
|
+
#### New Features
|
8
|
+
|
9
|
+
* Added `-E` option to disable embeddings for this chat session.
|
10
|
+
* Added `-M` option to load document embeddings into (empty) MemoryCache for this chat session.
|
11
|
+
* Added CSV parsing support to `ollama_chat`.
|
12
|
+
* Improved error handling in `Ollama::Utils::Fetcher` methods.
|
13
|
+
|
14
|
+
#### Bug Fixes
|
15
|
+
|
16
|
+
* Handle case in `ollama_chat` where responses don't provide counts, display 0
|
17
|
+
rates instead.
|
18
|
+
|
19
|
+
#### Refactoring and Improvements
|
20
|
+
|
21
|
+
* Updated eval count and rate display in FollowChat class.
|
22
|
+
* Refactor system prompt display and chunk listing in chat output.
|
23
|
+
* Refactor cache implementation to use Ollama::Documents::Cache::Common module.
|
24
|
+
* Improved system prompt formatting in `ollama_chat` script.
|
25
|
+
* Renamed `tags` method to `tags_set` in `Ollama::Documents::Record` class.
|
26
|
+
|
27
|
+
#### Documentation
|
28
|
+
|
29
|
+
* Added comments to ColorizeTexts utility class.
|
30
|
+
|
3
31
|
## 2024-09-15 v0.3.2
|
4
32
|
|
5
33
|
* Add color support to chooser module:
|
data/README.md
CHANGED
@@ -43,6 +43,8 @@ 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
|
+
-M use (empty) MemoryCache for this chat session
|
47
|
+
-E disable embeddings for this chat session
|
46
48
|
-v use voice output
|
47
49
|
-h this help
|
48
50
|
```
|
data/Rakefile
CHANGED
@@ -13,7 +13,8 @@ GemHadar do
|
|
13
13
|
description 'Library that allows interacting with the Ollama API'
|
14
14
|
test_dir 'spec'
|
15
15
|
ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
|
16
|
-
'.yardoc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
|
16
|
+
'.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
|
17
|
+
'corpus'
|
17
18
|
package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
|
18
19
|
'.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
|
19
20
|
readme 'README.md'
|
@@ -41,6 +42,8 @@ GemHadar do
|
|
41
42
|
development_dependency 'all_images', '~> 0.4'
|
42
43
|
development_dependency 'rspec', '~> 3.2'
|
43
44
|
development_dependency 'webmock'
|
45
|
+
development_dependency 'debug'
|
46
|
+
development_dependency 'simplecov'
|
44
47
|
|
45
48
|
licenses << 'MIT'
|
46
49
|
|
data/bin/ollama_chat
CHANGED
@@ -14,6 +14,7 @@ require 'uri'
|
|
14
14
|
require 'nokogiri'
|
15
15
|
require 'rss'
|
16
16
|
require 'pdf/reader'
|
17
|
+
require 'csv'
|
17
18
|
|
18
19
|
class OllamaChatConfig
|
19
20
|
include ComplexConfig
|
@@ -47,7 +48,7 @@ class OllamaChatConfig
|
|
47
48
|
splitter:
|
48
49
|
name: RecursiveCharacter
|
49
50
|
chunk_size: 1024
|
50
|
-
cache: Ollama::Documents::
|
51
|
+
cache: Ollama::Documents::Cache::RedisBackedMemoryCache
|
51
52
|
redis:
|
52
53
|
url: <%= ENV.fetch('REDIS_URL', 'null') %>
|
53
54
|
debug: <%= ENV['OLLAMA_CHAT_DEBUG'].to_i == 1 ? true : false %>
|
@@ -130,11 +131,11 @@ class FollowChat
|
|
130
131
|
prompt_eval_duration = response.prompt_eval_duration / 1e9
|
131
132
|
stats_text = {
|
132
133
|
eval_duration: Tins::Duration.new(eval_duration),
|
133
|
-
eval_count: response.eval_count,
|
134
|
-
eval_rate: bold { "%.2f c/s" % (response.eval_count / eval_duration) } + color(111),
|
134
|
+
eval_count: response.eval_count.to_i,
|
135
|
+
eval_rate: bold { "%.2f c/s" % (response.eval_count.to_i / eval_duration) } + color(111),
|
135
136
|
prompt_eval_duration: Tins::Duration.new(prompt_eval_duration),
|
136
|
-
prompt_eval_count: response.prompt_eval_count,
|
137
|
-
prompt_eval_rate: bold { "%.2f c/s" % (response.prompt_eval_count / prompt_eval_duration) } + color(111),
|
137
|
+
prompt_eval_count: response.prompt_eval_count.to_i,
|
138
|
+
prompt_eval_rate: bold { "%.2f c/s" % (response.prompt_eval_count.to_i / prompt_eval_duration) } + color(111),
|
138
139
|
total_duration: Tins::Duration.new(response.total_duration / 1e9),
|
139
140
|
load_duration: Tins::Duration.new(response.load_duration / 1e9),
|
140
141
|
}.map { _1 * '=' } * ' '
|
@@ -149,7 +150,7 @@ def search_web(query, n = nil)
|
|
149
150
|
n < 1 and n = 1
|
150
151
|
query = URI.encode_uri_component(query)
|
151
152
|
url = "https://www.duckduckgo.com/html/?q=#{query}"
|
152
|
-
Ollama::Utils::Fetcher.new.get(url) do |tmp|
|
153
|
+
Ollama::Utils::Fetcher.new(debug: $config.debug).get(url) do |tmp|
|
153
154
|
result = []
|
154
155
|
doc = Nokogiri::HTML(tmp)
|
155
156
|
doc.css('.results_links').each do |link|
|
@@ -302,6 +303,16 @@ def parse_source(source_io)
|
|
302
303
|
end
|
303
304
|
source_io.rewind
|
304
305
|
source_io.read
|
306
|
+
when 'text/csv'
|
307
|
+
result = +''
|
308
|
+
CSV.table(File.new(source_io), col_sep: ?,).each do |row|
|
309
|
+
next if row.fields.select(&:present?).size == 0
|
310
|
+
result << row.map { |pair|
|
311
|
+
pair.compact.map { _1.to_s.strip } * ': ' if pair.last.present?
|
312
|
+
}.select(&:present?).map { _1.prepend(' ') } * ?\n
|
313
|
+
result << "\n\n"
|
314
|
+
end
|
315
|
+
result
|
305
316
|
when %r(\Atext/)
|
306
317
|
source_io.read
|
307
318
|
when 'application/rss+xml'
|
@@ -312,11 +323,7 @@ def parse_source(source_io)
|
|
312
323
|
source_io.read
|
313
324
|
when 'application/pdf'
|
314
325
|
reader = PDF::Reader.new(source_io)
|
315
|
-
result
|
316
|
-
reader.pages.each do |page|
|
317
|
-
result << page.text
|
318
|
-
end
|
319
|
-
result
|
326
|
+
reader.pages.inject(+'') { |result, page| result << page.text }
|
320
327
|
else
|
321
328
|
STDERR.puts "Cannot import #{source_io&.content_type} document."
|
322
329
|
return
|
@@ -324,10 +331,7 @@ def parse_source(source_io)
|
|
324
331
|
end
|
325
332
|
|
326
333
|
def import_document(source_io, source)
|
327
|
-
|
328
|
-
STDOUT.puts "Embedding disabled, I won't import any documents, try: /summarize"
|
329
|
-
return
|
330
|
-
end
|
334
|
+
embedding_enabled? or return parse_source(source_io)
|
331
335
|
puts "Importing #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
|
332
336
|
text = parse_source(source_io) or return
|
333
337
|
text.downcase!
|
@@ -364,7 +368,7 @@ end
|
|
364
368
|
def fetch_source(source, &block)
|
365
369
|
case source
|
366
370
|
when %r(\Ahttps?://\S+)
|
367
|
-
Utils::Fetcher.get(source) do |tmp|
|
371
|
+
Utils::Fetcher.get(source, debug: $config.debug) do |tmp|
|
368
372
|
block.(tmp)
|
369
373
|
end
|
370
374
|
when %r(\Afile://(?:(?:[.-]|[[:alnum:]])*)(/\S*)|([~.]?/\S*))
|
@@ -384,7 +388,8 @@ def summarize(source)
|
|
384
388
|
puts "Now summarizing #{source.to_s.inspect}."
|
385
389
|
source_content =
|
386
390
|
fetch_source(source) do |source_io|
|
387
|
-
content = parse_source(source_io)
|
391
|
+
content = parse_source(source_io)
|
392
|
+
content.present? or return
|
388
393
|
source_io.rewind
|
389
394
|
import_document(source_io, source)
|
390
395
|
content
|
@@ -433,7 +438,7 @@ ensure
|
|
433
438
|
end
|
434
439
|
|
435
440
|
def choose_collection(default_collection)
|
436
|
-
collections = [ default_collection ] + $documents.collections
|
441
|
+
collections = [ default_collection ] + $documents.collections.map(&:to_s)
|
437
442
|
collections = collections.uniq.sort
|
438
443
|
$documents.collection = collection =
|
439
444
|
Ollama::Utils::Chooser.choose(collections) || default_collection
|
@@ -452,7 +457,11 @@ def collection_stats
|
|
452
457
|
end
|
453
458
|
|
454
459
|
def configure_cache
|
455
|
-
|
460
|
+
if $opts[?M]
|
461
|
+
Ollama::Documents::MemoryCache
|
462
|
+
else
|
463
|
+
Object.const_get($config.cache)
|
464
|
+
end
|
456
465
|
rescue => e
|
457
466
|
STDERR.puts "Caught #{e.class}: #{e} => Falling back to MemoryCache."
|
458
467
|
Ollama::Documents::MemoryCache
|
@@ -468,6 +477,14 @@ def set_markdown(value)
|
|
468
477
|
end
|
469
478
|
end
|
470
479
|
|
480
|
+
def clear_messages(messages)
|
481
|
+
messages.delete_if { _1.role != 'system' }
|
482
|
+
end
|
483
|
+
|
484
|
+
def embedding_enabled?
|
485
|
+
$config.embedding.enabled && !$opts[?E]
|
486
|
+
end
|
487
|
+
|
471
488
|
def display_chat_help
|
472
489
|
puts <<~end
|
473
490
|
/paste to paste content
|
@@ -498,7 +515,9 @@ def usage
|
|
498
515
|
-s SYSTEM the system prompt to use as a file, OLLAMA_CHAT_SYSTEM
|
499
516
|
-c CHAT a saved chat conversation to load
|
500
517
|
-C COLLECTION name of the collection used in this conversation
|
501
|
-
-D DOCUMENT load document and add to collection (multiple)
|
518
|
+
-D DOCUMENT load document and add to embeddings collection (multiple)
|
519
|
+
-M use (empty) MemoryCache for this chat session
|
520
|
+
-E disable embeddings for this chat session
|
502
521
|
-v use voice output
|
503
522
|
-h this help
|
504
523
|
|
@@ -510,28 +529,28 @@ def ollama
|
|
510
529
|
$ollama
|
511
530
|
end
|
512
531
|
|
513
|
-
opts = go 'f:u:m:s:c:C:D:
|
532
|
+
$opts = go 'f:u:m:s:c:C:D:MEvh'
|
514
533
|
|
515
|
-
config = OllamaChatConfig.new(opts[?f])
|
534
|
+
config = OllamaChatConfig.new($opts[?f])
|
516
535
|
$config = config.config
|
517
536
|
|
518
|
-
opts[?h] and usage
|
537
|
+
$opts[?h] and usage
|
519
538
|
|
520
539
|
puts "Configuration read from #{config.filename.inspect} is:", $config
|
521
540
|
|
522
|
-
base_url = opts[?u] || $config.url
|
541
|
+
base_url = $opts[?u] || $config.url
|
523
542
|
$ollama = Client.new(base_url:, debug: $config.debug)
|
524
543
|
|
525
|
-
model = choose_model(opts[?m], $config.model.name)
|
544
|
+
model = choose_model($opts[?m], $config.model.name)
|
526
545
|
options = Options[$config.model.options]
|
527
546
|
model_system = pull_model_unless_present(model, options)
|
528
547
|
messages = []
|
529
548
|
|
530
|
-
if
|
549
|
+
if embedding_enabled?
|
531
550
|
embedding_model = $config.embedding.model.name
|
532
551
|
embedding_model_options = Options[$config.embedding.model.options]
|
533
552
|
pull_model_unless_present(embedding_model, embedding_model_options)
|
534
|
-
collection = opts[?C] || $config.embedding.collection
|
553
|
+
collection = $opts[?C] || $config.embedding.collection
|
535
554
|
$documents = Documents.new(
|
536
555
|
ollama:,
|
537
556
|
model: $config.embedding.model.name,
|
@@ -541,7 +560,7 @@ if $config.embedding.enabled
|
|
541
560
|
redis_url: $config.redis.url?,
|
542
561
|
)
|
543
562
|
|
544
|
-
document_list = opts[?D].to_a
|
563
|
+
document_list = $opts[?D].to_a
|
545
564
|
if document_list.any?(&:empty?)
|
546
565
|
puts "Clearing collection #{bold{collection}}."
|
547
566
|
$documents.clear
|
@@ -569,18 +588,21 @@ else
|
|
569
588
|
$documents = Documents.new(ollama:, model:)
|
570
589
|
end
|
571
590
|
|
572
|
-
if voice = ($config.voice if opts[?v])
|
591
|
+
if voice = ($config.voice if $opts[?v])
|
573
592
|
puts "Using voice #{bold{voice}} to speak."
|
574
593
|
end
|
575
594
|
markdown = set_markdown($config.markdown)
|
576
595
|
|
577
|
-
if opts[?c]
|
578
|
-
messages.concat load_conversation(opts[?c])
|
596
|
+
if $opts[?c]
|
597
|
+
messages.concat load_conversation($opts[?c])
|
579
598
|
else
|
580
599
|
if system = Ollama::Utils::FileArgument.
|
581
|
-
get_file_argument(opts[?s], default: $config.prompts.system? || model_system)
|
600
|
+
get_file_argument($opts[?s], default: $config.prompts.system? || model_system)
|
582
601
|
messages << Message.new(role: 'system', content: system)
|
583
|
-
puts
|
602
|
+
puts <<~end
|
603
|
+
Configured system prompt is:
|
604
|
+
#{italic{Ollama::Utils::Width.wrap(system, percentage: 90)}}
|
605
|
+
end
|
584
606
|
end
|
585
607
|
end
|
586
608
|
|
@@ -606,11 +628,11 @@ loop do
|
|
606
628
|
list_conversation(messages, markdown)
|
607
629
|
next
|
608
630
|
when %r(^/clear$)
|
609
|
-
messages
|
631
|
+
clear_messages(messages)
|
610
632
|
puts "Cleared messages."
|
611
633
|
next
|
612
634
|
when %r(^/clobber$)
|
613
|
-
messages
|
635
|
+
clear_messages(messages)
|
614
636
|
$documents.clear
|
615
637
|
puts "Cleared messages and collection."
|
616
638
|
next
|
@@ -697,7 +719,7 @@ loop do
|
|
697
719
|
[ content, Utils::Tags.new ]
|
698
720
|
end
|
699
721
|
|
700
|
-
if
|
722
|
+
if embedding_enabled? && content
|
701
723
|
records = $documents.find_where(
|
702
724
|
content.downcase,
|
703
725
|
tags:,
|
@@ -705,10 +727,9 @@ loop do
|
|
705
727
|
text_size: $config.embedding.found_texts_size?,
|
706
728
|
text_count: $config.embedding.found_texts_count?,
|
707
729
|
)
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
"#{found_texts.join("\n\n---\n\n")}"
|
730
|
+
unless records.empty?
|
731
|
+
content += "\nConsider these chunks for your answer:\n\n"\
|
732
|
+
"#{records.map { [ _1.text, _1.tags_set ] * ?\n }.join("\n\n---\n\n")}"
|
712
733
|
end
|
713
734
|
end
|
714
735
|
|
@@ -716,8 +737,8 @@ loop do
|
|
716
737
|
handler = FollowChat.new(messages:, markdown:, voice:)
|
717
738
|
ollama.chat(model:, messages:, options:, stream: true, &handler)
|
718
739
|
|
719
|
-
if records
|
720
|
-
puts records.map { |record|
|
740
|
+
if embedding_enabled? && !records.empty?
|
741
|
+
puts "", records.map { |record|
|
721
742
|
link = if record.source =~ %r(\Ahttps?://)
|
722
743
|
record.source
|
723
744
|
else
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ollama::Documents::Cache::Common
|
2
|
+
attr_writer :prefix
|
3
|
+
|
4
|
+
def collections(prefix)
|
5
|
+
unique = Set.new
|
6
|
+
full_each { |key, _| unique << key[/\A#{prefix}(.*)-/, 1] }
|
7
|
+
unique.map(&:to_sym)
|
8
|
+
end
|
9
|
+
|
10
|
+
def pre(key)
|
11
|
+
[ @prefix, key ].join
|
12
|
+
end
|
13
|
+
|
14
|
+
def unpre(key)
|
15
|
+
key.sub(/\A#@prefix/, '')
|
16
|
+
end
|
17
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
require 'ollama/documents/cache/common'
|
2
|
+
|
1
3
|
class Ollama::Documents::MemoryCache
|
4
|
+
include Ollama::Documents::Cache::Common
|
5
|
+
|
2
6
|
def initialize(prefix:)
|
3
7
|
@prefix = prefix
|
4
8
|
@data = {}
|
5
9
|
end
|
6
10
|
|
7
|
-
attr_writer :prefix
|
8
|
-
|
9
11
|
def [](key)
|
10
12
|
@data[pre(key)]
|
11
13
|
end
|
@@ -23,11 +25,11 @@ class Ollama::Documents::MemoryCache
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def size
|
26
|
-
|
28
|
+
count
|
27
29
|
end
|
28
30
|
|
29
31
|
def clear
|
30
|
-
@data.
|
32
|
+
@data.delete_if { |key, _| key.start_with?(@prefix) }
|
31
33
|
self
|
32
34
|
end
|
33
35
|
|
@@ -36,11 +38,7 @@ class Ollama::Documents::MemoryCache
|
|
36
38
|
end
|
37
39
|
include Enumerable
|
38
40
|
|
39
|
-
def
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def unpre(key)
|
44
|
-
key.sub(/\A#@prefix/, '')
|
41
|
+
def full_each(&block)
|
42
|
+
@data.each(&block)
|
45
43
|
end
|
46
44
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
class Ollama::Documents
|
4
|
+
class RedisBackedMemoryCache < MemoryCache
|
5
|
+
def initialize(prefix:, url: ENV['REDIS_URL'])
|
6
|
+
super(prefix:)
|
7
|
+
url or raise ArgumentError, 'require redis url'
|
8
|
+
@prefix, @url = prefix, url
|
9
|
+
@redis_cache = Ollama::Documents::RedisCache.new(prefix:, url:)
|
10
|
+
@redis_cache.full_each do |key, value|
|
11
|
+
@data[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def redis
|
16
|
+
@redis_cache.redis
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
super
|
21
|
+
redis.set(pre(key), JSON(value))
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(key)
|
25
|
+
result = redis.del(pre(key))
|
26
|
+
super
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear
|
31
|
+
redis.scan_each(match: "#@prefix*") { |key| redis.del(key) }
|
32
|
+
super
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def pre(key)
|
37
|
+
[ @prefix, key ].join
|
38
|
+
end
|
39
|
+
|
40
|
+
def unpre(key)
|
41
|
+
key.sub(/\A#@prefix/, '')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
require 'ollama/documents/cache/common'
|
1
2
|
require 'redis'
|
2
3
|
|
3
4
|
class Ollama::Documents::RedisCache
|
5
|
+
include Ollama::Documents::Cache::Common
|
6
|
+
|
4
7
|
def initialize(prefix:, url: ENV['REDIS_URL'])
|
5
8
|
url or raise ArgumentError, 'require redis url'
|
6
9
|
@prefix, @url = prefix, url
|
7
10
|
end
|
8
11
|
|
9
|
-
attr_writer :prefix
|
10
|
-
|
11
12
|
def redis
|
12
13
|
@redis ||= Redis.new(url: @url)
|
13
14
|
end
|
@@ -48,11 +49,11 @@ class Ollama::Documents::RedisCache
|
|
48
49
|
end
|
49
50
|
include Enumerable
|
50
51
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def full_each(&block)
|
53
|
+
redis.scan_each do |key|
|
54
|
+
value = redis.get(key) or next
|
55
|
+
value = JSON(value, object_class: Ollama::Documents::Record)
|
56
|
+
block.(key, value)
|
57
|
+
end
|
57
58
|
end
|
58
59
|
end
|
data/lib/ollama/documents.rb
CHANGED
@@ -3,8 +3,11 @@ require 'digest'
|
|
3
3
|
|
4
4
|
class Ollama::Documents
|
5
5
|
end
|
6
|
-
|
7
|
-
|
6
|
+
class Ollama::Documents::Cache
|
7
|
+
end
|
8
|
+
require 'ollama/documents/cache/memory_cache'
|
9
|
+
require 'ollama/documents/cache/redis_cache'
|
10
|
+
require 'ollama/documents/cache/redis_backed_memory_cache'
|
8
11
|
module Ollama::Documents::Splitters
|
9
12
|
end
|
10
13
|
require 'ollama/documents/splitters/character'
|
@@ -16,11 +19,15 @@ class Ollama::Documents
|
|
16
19
|
|
17
20
|
class Record < JSON::GenericObject
|
18
21
|
def to_s
|
19
|
-
my_tags =
|
22
|
+
my_tags = tags_set
|
20
23
|
my_tags.empty? or my_tags = " #{my_tags}"
|
21
24
|
"#<#{self.class} #{text.inspect}#{my_tags} #{similarity || 'n/a'}>"
|
22
25
|
end
|
23
26
|
|
27
|
+
def tags_set
|
28
|
+
Ollama::Utils::Tags.new(tags)
|
29
|
+
end
|
30
|
+
|
24
31
|
def ==(other)
|
25
32
|
text == other.text
|
26
33
|
end
|
@@ -28,15 +35,19 @@ class Ollama::Documents
|
|
28
35
|
alias inspect to_s
|
29
36
|
end
|
30
37
|
|
31
|
-
def initialize(ollama:, model:, model_options: nil, collection:
|
32
|
-
@ollama, @model, @model_options, @collection = ollama, model, model_options, collection
|
38
|
+
def initialize(ollama:, model:, model_options: nil, collection: default_collection, cache: MemoryCache, redis_url: nil)
|
39
|
+
@ollama, @model, @model_options, @collection = ollama, model, model_options, collection.to_sym
|
33
40
|
@cache, @redis_url = connect_cache(cache), redis_url
|
34
41
|
end
|
35
42
|
|
43
|
+
def default_collection
|
44
|
+
:default
|
45
|
+
end
|
46
|
+
|
36
47
|
attr_reader :ollama, :model, :collection
|
37
48
|
|
38
49
|
def collection=(new_collection)
|
39
|
-
@collection
|
50
|
+
@collection = new_collection.to_sym
|
40
51
|
@cache.prefix = prefix
|
41
52
|
end
|
42
53
|
|
@@ -137,15 +148,7 @@ class Ollama::Documents
|
|
137
148
|
end
|
138
149
|
|
139
150
|
def collections
|
140
|
-
|
141
|
-
when MemoryCache
|
142
|
-
[ @collection ]
|
143
|
-
when RedisCache
|
144
|
-
prefix = '%s-' % self.class
|
145
|
-
Documents::RedisCache.new(prefix:, url: @redis_url).map { _1[/#{prefix}(.*)-/, 1] }.uniq
|
146
|
-
else
|
147
|
-
[]
|
148
|
-
end
|
151
|
+
([ default_collection ] + @cache.collections('%s-' % self.class)).uniq
|
149
152
|
end
|
150
153
|
|
151
154
|
def tags
|
@@ -156,7 +159,7 @@ class Ollama::Documents
|
|
156
159
|
|
157
160
|
def connect_cache(cache_class)
|
158
161
|
cache = nil
|
159
|
-
if cache_class
|
162
|
+
if cache_class.instance_method(:redis)
|
160
163
|
begin
|
161
164
|
cache = cache_class.new(prefix:)
|
162
165
|
cache.size
|
@@ -3,11 +3,20 @@ class Ollama::Utils::ColorizeTexts
|
|
3
3
|
include Term::ANSIColor
|
4
4
|
include Ollama::Utils::Width
|
5
5
|
|
6
|
+
# Initializes a new instance of Ollama::Utils::ColorizeTexts
|
7
|
+
#
|
8
|
+
# @param [Array<String>] texts the array of strings to be displayed with colors
|
9
|
+
#
|
10
|
+
# @return [Ollama::Utils::ColorizeTexts] an instance of Ollama::Utils::ColorizeTexts
|
6
11
|
def initialize(*texts)
|
7
|
-
texts
|
12
|
+
texts = texts.map(&:to_a)
|
8
13
|
@texts = Array(texts.flatten)
|
9
14
|
end
|
10
15
|
|
16
|
+
# Returns a string representation of the object, including all texts content,
|
17
|
+
# colored differently and their sizes.
|
18
|
+
#
|
19
|
+
# @return [String] The formatted string.
|
11
20
|
def to_s
|
12
21
|
result = +''
|
13
22
|
@texts.each_with_index do |t, i|
|
@@ -22,6 +31,14 @@ class Ollama::Utils::ColorizeTexts
|
|
22
31
|
|
23
32
|
private
|
24
33
|
|
34
|
+
# Returns the nearest RGB color to the given ANSI color
|
35
|
+
#
|
36
|
+
# @param [color] color The ANSI color attribute
|
37
|
+
#
|
38
|
+
# @return [Array<RGBTriple>] An array containing two RGB colors, one for black and
|
39
|
+
# one for white text, where the first is the closest match to the input color
|
40
|
+
# when printed on a black background, and the second is the closest match
|
41
|
+
# when printed on a white background.
|
25
42
|
def text_color(color)
|
26
43
|
color = Term::ANSIColor::Attribute[color]
|
27
44
|
[
|
@@ -30,6 +47,9 @@ class Ollama::Utils::ColorizeTexts
|
|
30
47
|
].max_by { |t| t.distance_to(color) }
|
31
48
|
end
|
32
49
|
|
50
|
+
# Returns an array of colors for each step in the gradient
|
51
|
+
#
|
52
|
+
# @return [Array<Array<Integer>>] An array of RGB color arrays
|
33
53
|
def colors
|
34
54
|
@colors ||= (0..255).map { |i|
|
35
55
|
[
|
data/lib/ollama/utils/fetcher.rb
CHANGED
@@ -2,6 +2,7 @@ require 'tempfile'
|
|
2
2
|
require 'tins/unit'
|
3
3
|
require 'infobar'
|
4
4
|
require 'mime-types'
|
5
|
+
require 'stringio'
|
5
6
|
|
6
7
|
class Ollama::Utils::Fetcher
|
7
8
|
module ContentType
|
@@ -10,13 +11,14 @@ class Ollama::Utils::Fetcher
|
|
10
11
|
|
11
12
|
class RetryWithoutStreaming < StandardError; end
|
12
13
|
|
13
|
-
def initialize
|
14
|
+
def initialize(debug: false)
|
15
|
+
@debug = debug
|
14
16
|
@started = false
|
15
17
|
@streaming = true
|
16
18
|
end
|
17
19
|
|
18
|
-
def self.get(url, &block)
|
19
|
-
new.get(url, &block)
|
20
|
+
def self.get(url, **options, &block)
|
21
|
+
new(**options).get(url, &block)
|
20
22
|
end
|
21
23
|
|
22
24
|
def get(url, &block)
|
@@ -46,11 +48,11 @@ class Ollama::Utils::Fetcher
|
|
46
48
|
@streaming = false
|
47
49
|
retry
|
48
50
|
rescue => e
|
49
|
-
STDERR.puts "Cannot get #{url.to_s.inspect} (#{e}): #{response&.status_line}"
|
50
|
-
|
51
|
+
STDERR.puts "Cannot get #{url.to_s.inspect} (#{e}): #{response&.status_line || 'n/a'}"
|
52
|
+
if @debug && !e.is_a?(RuntimeError)
|
51
53
|
STDERR.puts "#{e.backtrace * ?\n}"
|
52
54
|
end
|
53
|
-
yield
|
55
|
+
yield StringIO.new.extend(ContentType)
|
54
56
|
end
|
55
57
|
|
56
58
|
def headers
|
@@ -1,6 +1,24 @@
|
|
1
1
|
module Ollama::Utils::FileArgument
|
2
2
|
module_function
|
3
3
|
|
4
|
+
# Returns the contents of a file or string, or a default value if neither is provided.
|
5
|
+
#
|
6
|
+
# @param [String] path_or_content The path to a file or a string containing
|
7
|
+
# the content.
|
8
|
+
#
|
9
|
+
# @param [String] default The default value to return if no valid input is
|
10
|
+
# given. Defaults to nil.
|
11
|
+
#
|
12
|
+
# @return [String] The contents of the file, the string, or the default value.
|
13
|
+
#
|
14
|
+
# @example Get the contents of a file
|
15
|
+
# get_file_argument('path/to/file')
|
16
|
+
#
|
17
|
+
# @example Use a string as content
|
18
|
+
# get_file_argument('string content')
|
19
|
+
#
|
20
|
+
# @example Return a default value if no valid input is given
|
21
|
+
# get_file_argument(nil, default: 'default content')
|
4
22
|
def get_file_argument(path_or_content, default: nil)
|
5
23
|
if path_or_content.present? && path_or_content.size < 2 ** 15 &&
|
6
24
|
File.basename(path_or_content).size < 2 ** 8 &&
|
data/lib/ollama/utils/tags.rb
CHANGED
data/lib/ollama/version.rb
CHANGED
data/ollama-ruby.gemspec
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: ollama-ruby 0.
|
2
|
+
# stub: ollama-ruby 0.4.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.4.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-09-
|
11
|
+
s.date = "2024-09-21"
|
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]
|
15
|
-
s.extra_rdoc_files = ["README.md".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/memory_cache.rb".freeze, "lib/ollama/documents/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze]
|
16
|
-
s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/memory_cache.rb".freeze, "lib/ollama/documents/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
|
15
|
+
s.extra_rdoc_files = ["README.md".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze]
|
16
|
+
s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
|
17
17
|
s.homepage = "https://github.com/flori/ollama-ruby".freeze
|
18
18
|
s.licenses = ["MIT".freeze]
|
19
19
|
s.rdoc_options = ["--title".freeze, "Ollama-ruby - Interacting with the Ollama API".freeze, "--main".freeze, "README.md".freeze]
|
20
20
|
s.required_ruby_version = Gem::Requirement.new("~> 3.1".freeze)
|
21
21
|
s.rubygems_version = "3.5.18".freeze
|
22
22
|
s.summary = "Interacting with the Ollama API".freeze
|
23
|
-
s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze]
|
23
|
+
s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/spec_helper.rb".freeze]
|
24
24
|
|
25
25
|
s.specification_version = 4
|
26
26
|
|
@@ -28,6 +28,8 @@ Gem::Specification.new do |s|
|
|
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])
|
31
|
+
s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
|
32
|
+
s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
|
31
33
|
s.add_runtime_dependency(%q<excon>.freeze, ["~> 0.111".freeze])
|
32
34
|
s.add_runtime_dependency(%q<infobar>.freeze, ["~> 0.8".freeze])
|
33
35
|
s.add_runtime_dependency(%q<term-ansicolor>.freeze, ["~> 1.11".freeze])
|
@@ -1,63 +1,63 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Ollama::Documents::MemoryCache do
|
4
|
-
let :
|
4
|
+
let :cache do
|
5
5
|
described_class.new prefix: 'test-'
|
6
6
|
end
|
7
7
|
|
8
8
|
it 'can be instantiated' do
|
9
|
-
expect(
|
9
|
+
expect(cache).to be_a described_class
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'can get/set a key' do
|
13
13
|
key, value = 'foo', { test: true }
|
14
14
|
expect {
|
15
|
-
|
15
|
+
cache[key] = value
|
16
16
|
}.to change {
|
17
|
-
|
17
|
+
cache[key]
|
18
18
|
}.from(nil).to(value)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'can determine if key exists' do
|
22
22
|
key, value = 'foo', { test: true }
|
23
23
|
expect {
|
24
|
-
|
24
|
+
cache[key] = value
|
25
25
|
}.to change {
|
26
|
-
|
26
|
+
cache.key?(key)
|
27
27
|
}.from(false).to(true)
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'can delete' do
|
31
31
|
key, value = 'foo', { test: true }
|
32
|
-
|
32
|
+
cache[key] = value
|
33
33
|
expect {
|
34
|
-
|
34
|
+
cache.delete(key)
|
35
35
|
}.to change {
|
36
|
-
|
36
|
+
cache.key?(key)
|
37
37
|
}.from(true).to(false)
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'returns size' do
|
41
41
|
key, value = 'foo', { test: true }
|
42
42
|
expect {
|
43
|
-
|
43
|
+
cache[key] = value
|
44
44
|
}.to change {
|
45
|
-
|
45
|
+
cache.size
|
46
46
|
}.from(0).to(1)
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'can clear' do
|
50
50
|
key, value = 'foo', { test: true }
|
51
|
-
|
51
|
+
cache[key] = value
|
52
52
|
expect {
|
53
|
-
expect(
|
53
|
+
expect(cache.clear).to eq cache
|
54
54
|
}.to change {
|
55
|
-
|
55
|
+
cache.size
|
56
56
|
}.from(1).to(0)
|
57
57
|
end
|
58
58
|
|
59
59
|
it 'can iterate over keys under a prefix' do
|
60
|
-
|
61
|
-
expect(
|
60
|
+
cache['foo'] = 'bar'
|
61
|
+
expect(cache.to_a).to eq [ %w[ test-foo bar ] ]
|
62
62
|
end
|
63
63
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Ollama::Documents::RedisBackedMemoryCache do
|
4
|
+
it 'raises ArgumentError if url is missing' do
|
5
|
+
expect {
|
6
|
+
described_class.new prefix: 'test-', url: nil
|
7
|
+
}.to raise_error ArgumentError
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'test redis interactions' do
|
11
|
+
let :cache do
|
12
|
+
described_class.new prefix: 'test-', url: 'something'
|
13
|
+
end
|
14
|
+
|
15
|
+
let :data do
|
16
|
+
cache.instance_eval { @data }
|
17
|
+
end
|
18
|
+
|
19
|
+
let :redis_cache do
|
20
|
+
cache.instance_eval { @redis_cache }
|
21
|
+
end
|
22
|
+
|
23
|
+
let :redis do
|
24
|
+
double('Redis')
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
allow_any_instance_of(Ollama::Documents::RedisCache).to\
|
29
|
+
receive(:redis).and_return(redis)
|
30
|
+
allow(redis).to receive(:scan_each)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can be instantiated and initialized' do
|
34
|
+
cache = described_class.new prefix: 'test-', url: 'something'
|
35
|
+
expect(cache).to be_a described_class
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has Redis client' do
|
39
|
+
expect(cache.redis).to eq redis
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'can get a key' do
|
43
|
+
key = 'foo'
|
44
|
+
expect(data).to receive(:[]).with('test-' + key).and_return 666
|
45
|
+
expect(cache[key]).to eq 666
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can set a value for a key' do
|
49
|
+
key, value = 'foo', { test: true }
|
50
|
+
expect(data).to receive(:[]=).with('test-' + key, { test: true }).and_call_original
|
51
|
+
expect(redis).to receive(:set).with('test-' + key, JSON(value))
|
52
|
+
cache[key] = value
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'can determine if key exists' do
|
56
|
+
key = 'foo'
|
57
|
+
expect(data).to receive(:key?).with('test-' + key).and_return(false, true)
|
58
|
+
expect(cache.key?('foo')).to eq false
|
59
|
+
expect(cache.key?('foo')).to eq true
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can delete' do
|
63
|
+
key = 'foo'
|
64
|
+
expect(data).to receive(:delete).with('test-' + key)
|
65
|
+
expect(redis).to receive(:del).with('test-' + key)
|
66
|
+
cache.delete(key)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns size' do
|
70
|
+
allow(cache).to receive(:count).and_return 3
|
71
|
+
expect(cache.size).to eq 3
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can clear' do
|
75
|
+
expect(redis).to receive(:scan_each).with(match: 'test-*').and_yield(
|
76
|
+
'test-foo'
|
77
|
+
)
|
78
|
+
expect(redis).to receive(:del).with('test-foo')
|
79
|
+
expect(cache.clear).to eq cache
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'can iterate over keys under a prefix' do
|
83
|
+
data['test-foo'] = 'bar'
|
84
|
+
expect(cache.to_a).to eq [ %w[ test-foo bar ] ]
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'can compute prefix with pre' do
|
88
|
+
expect(cache.pre('foo')).to eq 'test-foo'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'can remove prefix with unpre' do
|
92
|
+
expect(cache.unpre('test-foo')).to eq 'foo'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Ollama::Documents::RedisCache do
|
4
4
|
it 'can be instantiated' do
|
5
|
-
|
6
|
-
expect(
|
5
|
+
cache = described_class.new prefix: 'test-', url: 'something'
|
6
|
+
expect(cache).to be_a described_class
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'raises ArgumentError if url is missing' do
|
@@ -13,7 +13,7 @@ RSpec.describe Ollama::Documents::RedisCache do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'test redis interactions' do
|
16
|
-
let :
|
16
|
+
let :cache do
|
17
17
|
described_class.new prefix: 'test-', url: 'something'
|
18
18
|
end
|
19
19
|
|
@@ -26,32 +26,32 @@ RSpec.describe Ollama::Documents::RedisCache do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'has Redis client' do
|
29
|
-
expect(
|
29
|
+
expect(cache.redis).to eq redis
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'can get a key' do
|
33
33
|
key = 'foo'
|
34
|
-
expect(redis).to receive(:get).with('test-' + key).and_return
|
35
|
-
|
34
|
+
expect(redis).to receive(:get).with('test-' + key).and_return '"some_json"'
|
35
|
+
expect(cache[key]).to eq 'some_json'
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'can set a value for a key' do
|
39
39
|
key, value = 'foo', { test: true }
|
40
40
|
expect(redis).to receive(:set).with('test-' + key, JSON(value))
|
41
|
-
|
41
|
+
cache[key] = value
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'can determine if key exists' do
|
45
45
|
key = 'foo'
|
46
46
|
expect(redis).to receive(:exists?).with('test-' + key).and_return(false, true)
|
47
|
-
expect(
|
48
|
-
expect(
|
47
|
+
expect(cache.key?('foo')).to eq false
|
48
|
+
expect(cache.key?('foo')).to eq true
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'can delete' do
|
52
52
|
key = 'foo'
|
53
53
|
expect(redis).to receive(:del).with('test-' + key)
|
54
|
-
|
54
|
+
cache.delete(key)
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'returns size' do
|
@@ -59,7 +59,7 @@ RSpec.describe Ollama::Documents::RedisCache do
|
|
59
59
|
and_yield('test-foo').
|
60
60
|
and_yield('test-bar').
|
61
61
|
and_yield('test-baz')
|
62
|
-
expect(
|
62
|
+
expect(cache.size).to eq 3
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'can clear' do
|
@@ -67,20 +67,20 @@ RSpec.describe Ollama::Documents::RedisCache do
|
|
67
67
|
'test-foo'
|
68
68
|
)
|
69
69
|
expect(redis).to receive(:del).with('test-foo')
|
70
|
-
expect(
|
70
|
+
expect(cache.clear).to eq cache
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'can iterate over keys under a prefix' do
|
74
74
|
expect(redis).to receive(:scan_each).with(match: 'test-*')
|
75
|
-
|
75
|
+
cache.to_a
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'can compute prefix with pre' do
|
79
|
-
expect(
|
79
|
+
expect(cache.pre('foo')).to eq 'test-foo'
|
80
80
|
end
|
81
81
|
|
82
82
|
it 'can remove prefix with unpre' do
|
83
|
-
expect(
|
83
|
+
expect(cache.unpre('test-foo')).to eq 'foo'
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
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.4.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-09-
|
11
|
+
date: 2024-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem_hadar
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: debug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: excon
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -303,8 +331,10 @@ extra_rdoc_files:
|
|
303
331
|
- lib/ollama/commands/show.rb
|
304
332
|
- lib/ollama/commands/tags.rb
|
305
333
|
- lib/ollama/documents.rb
|
306
|
-
- lib/ollama/documents/
|
307
|
-
- lib/ollama/documents/
|
334
|
+
- lib/ollama/documents/cache/common.rb
|
335
|
+
- lib/ollama/documents/cache/memory_cache.rb
|
336
|
+
- lib/ollama/documents/cache/redis_backed_memory_cache.rb
|
337
|
+
- lib/ollama/documents/cache/redis_cache.rb
|
308
338
|
- lib/ollama/documents/splitters/character.rb
|
309
339
|
- lib/ollama/documents/splitters/semantic.rb
|
310
340
|
- lib/ollama/dto.rb
|
@@ -367,8 +397,10 @@ files:
|
|
367
397
|
- lib/ollama/commands/show.rb
|
368
398
|
- lib/ollama/commands/tags.rb
|
369
399
|
- lib/ollama/documents.rb
|
370
|
-
- lib/ollama/documents/
|
371
|
-
- lib/ollama/documents/
|
400
|
+
- lib/ollama/documents/cache/common.rb
|
401
|
+
- lib/ollama/documents/cache/memory_cache.rb
|
402
|
+
- lib/ollama/documents/cache/redis_backed_memory_cache.rb
|
403
|
+
- lib/ollama/documents/cache/redis_cache.rb
|
372
404
|
- lib/ollama/documents/splitters/character.rb
|
373
405
|
- lib/ollama/documents/splitters/semantic.rb
|
374
406
|
- lib/ollama/dto.rb
|
@@ -420,6 +452,7 @@ files:
|
|
420
452
|
- spec/ollama/commands/show_spec.rb
|
421
453
|
- spec/ollama/commands/tags_spec.rb
|
422
454
|
- spec/ollama/documents/memory_cache_spec.rb
|
455
|
+
- spec/ollama/documents/redis_backed_memory_cache_spec.rb
|
423
456
|
- spec/ollama/documents/redis_cache_spec.rb
|
424
457
|
- spec/ollama/documents/splitters/character_spec.rb
|
425
458
|
- spec/ollama/documents/splitters/semantic_spec.rb
|
@@ -486,6 +519,7 @@ test_files:
|
|
486
519
|
- spec/ollama/commands/show_spec.rb
|
487
520
|
- spec/ollama/commands/tags_spec.rb
|
488
521
|
- spec/ollama/documents/memory_cache_spec.rb
|
522
|
+
- spec/ollama/documents/redis_backed_memory_cache_spec.rb
|
489
523
|
- spec/ollama/documents/redis_cache_spec.rb
|
490
524
|
- spec/ollama/documents/splitters/character_spec.rb
|
491
525
|
- spec/ollama/documents/splitters/semantic_spec.rb
|