ollama_chat 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.all_images.yml +17 -0
- data/.gitignore +9 -0
- data/Gemfile +5 -0
- data/README.md +159 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/bin/ollama_chat +5 -0
- data/lib/ollama_chat/chat.rb +398 -0
- data/lib/ollama_chat/clipboard.rb +23 -0
- data/lib/ollama_chat/dialog.rb +94 -0
- data/lib/ollama_chat/document_cache.rb +16 -0
- data/lib/ollama_chat/follow_chat.rb +60 -0
- data/lib/ollama_chat/information.rb +113 -0
- data/lib/ollama_chat/message_list.rb +216 -0
- data/lib/ollama_chat/message_type.rb +5 -0
- data/lib/ollama_chat/model_handling.rb +29 -0
- data/lib/ollama_chat/ollama_chat_config.rb +103 -0
- data/lib/ollama_chat/parsing.rb +159 -0
- data/lib/ollama_chat/source_fetching.rb +173 -0
- data/lib/ollama_chat/switches.rb +119 -0
- data/lib/ollama_chat/utils/cache_fetcher.rb +38 -0
- data/lib/ollama_chat/utils/chooser.rb +53 -0
- data/lib/ollama_chat/utils/fetcher.rb +175 -0
- data/lib/ollama_chat/utils/file_argument.rb +34 -0
- data/lib/ollama_chat/utils.rb +7 -0
- data/lib/ollama_chat/version.rb +8 -0
- data/lib/ollama_chat.rb +20 -0
- data/ollama_chat.gemspec +50 -0
- data/spec/assets/api_show.json +63 -0
- data/spec/assets/api_tags.json +21 -0
- data/spec/assets/conversation.json +14 -0
- data/spec/assets/duckduckgo.html +757 -0
- data/spec/assets/example.atom +26 -0
- data/spec/assets/example.csv +5 -0
- data/spec/assets/example.html +10 -0
- data/spec/assets/example.pdf +139 -0
- data/spec/assets/example.ps +4 -0
- data/spec/assets/example.rb +1 -0
- data/spec/assets/example.rss +25 -0
- data/spec/assets/example.xml +7 -0
- data/spec/assets/kitten.jpg +0 -0
- data/spec/assets/prompt.txt +1 -0
- data/spec/ollama_chat/chat_spec.rb +105 -0
- data/spec/ollama_chat/clipboard_spec.rb +29 -0
- data/spec/ollama_chat/follow_chat_spec.rb +46 -0
- data/spec/ollama_chat/information_spec.rb +50 -0
- data/spec/ollama_chat/message_list_spec.rb +132 -0
- data/spec/ollama_chat/model_handling_spec.rb +35 -0
- data/spec/ollama_chat/parsing_spec.rb +240 -0
- data/spec/ollama_chat/source_fetching_spec.rb +54 -0
- data/spec/ollama_chat/switches_spec.rb +167 -0
- data/spec/ollama_chat/utils/cache_fetcher_spec.rb +43 -0
- data/spec/ollama_chat/utils/fetcher_spec.rb +137 -0
- data/spec/ollama_chat/utils/file_argument_spec.rb +17 -0
- data/spec/spec_helper.rb +46 -0
- data/tmp/.keep +0 -0
- metadata +476 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
RSpec.describe OllamaChat::Parsing do
|
5
|
+
let :chat do
|
6
|
+
OllamaChat::Chat.new
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
stub_request(:get, %r(/api/tags\z)).
|
11
|
+
to_return(status: 200, body: asset_json('api_tags.json'))
|
12
|
+
stub_request(:post, %r(/api/show\z)).
|
13
|
+
to_return(status: 200, body: asset_json('api_show.json'))
|
14
|
+
chat
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#parse_source' do
|
18
|
+
it 'can parse HTML' do
|
19
|
+
asset_io('example.html') do |io|
|
20
|
+
def io.content_type
|
21
|
+
'text/html'
|
22
|
+
end
|
23
|
+
expect(chat.parse_source(io)).to eq(
|
24
|
+
"# My First Heading\n\nMy first paragraph.\n\n"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can parse XML' do
|
30
|
+
asset_io('example.xml') do |io|
|
31
|
+
def io.content_type
|
32
|
+
'text/xml'
|
33
|
+
end
|
34
|
+
expect(chat.parse_source(io)).to eq(asset_content('example.xml'))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can parse CSV' do
|
39
|
+
asset_io('example.csv') do |io|
|
40
|
+
def io.content_type
|
41
|
+
'text/csv'
|
42
|
+
end
|
43
|
+
expect(chat.parse_source(io)).to eq(<<EOT)
|
44
|
+
name: John Doe
|
45
|
+
age: 32
|
46
|
+
occupation: Software Engineer
|
47
|
+
|
48
|
+
name: Jane Smith
|
49
|
+
age: 28
|
50
|
+
occupation: Marketing Manager
|
51
|
+
|
52
|
+
name: Bob Johnson
|
53
|
+
age: 45
|
54
|
+
occupation: Retired
|
55
|
+
|
56
|
+
name: Alice Brown
|
57
|
+
age: 25
|
58
|
+
occupation: Student
|
59
|
+
|
60
|
+
EOT
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'can parse RSS' do
|
65
|
+
asset_io('example.rss') do |io|
|
66
|
+
def io.content_type
|
67
|
+
'application/rss+xml'
|
68
|
+
end
|
69
|
+
expect(chat.parse_source(io)).to start_with(<<~EOT)
|
70
|
+
# Example News Feed
|
71
|
+
|
72
|
+
## [New Study Shows Benefits of Meditation](https://example.com/article/meditation-benefits)
|
73
|
+
|
74
|
+
EOT
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'can parse RSS with content type XML' do
|
79
|
+
asset_io('example.rss') do |io|
|
80
|
+
def io.content_type
|
81
|
+
'text/xml'
|
82
|
+
end
|
83
|
+
expect(chat.parse_source(io)).to start_with(<<~EOT)
|
84
|
+
# Example News Feed
|
85
|
+
|
86
|
+
## [New Study Shows Benefits of Meditation](https://example.com/article/meditation-benefits)
|
87
|
+
|
88
|
+
EOT
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'can parse Atom' do
|
93
|
+
asset_io('example.atom') do |io|
|
94
|
+
def io.content_type
|
95
|
+
'application/atom+xml'
|
96
|
+
end
|
97
|
+
expect(chat.parse_source(io)).to start_with(<<~EOT)
|
98
|
+
# Example Feed
|
99
|
+
|
100
|
+
## [New Study Shows Benefits of Meditation](https://example.com/article/meditation-benefits)
|
101
|
+
|
102
|
+
updated on 2024-01-01T12:00:00Z
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
## [Local Business Opens New Location](https://example.com/article/local-business-new-location)
|
107
|
+
|
108
|
+
updated on 2024-01-02T10:00:00Z
|
109
|
+
EOT
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'can parse Postscript' do
|
114
|
+
asset_io('example.ps') do |io|
|
115
|
+
def io.content_type
|
116
|
+
'application/postscript'
|
117
|
+
end
|
118
|
+
expect(chat.parse_source(io)).to eq("Hello World!")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'can parse PDF' do
|
123
|
+
asset_io('example.pdf') do |io|
|
124
|
+
def io.content_type
|
125
|
+
'application/pdf'
|
126
|
+
end
|
127
|
+
expect(chat.parse_source(io)).to eq("Hello World!")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'can parse other texts' do
|
132
|
+
asset_io('example.rb') do |io|
|
133
|
+
def io.content_type
|
134
|
+
'application/x-ruby'
|
135
|
+
end
|
136
|
+
expect(chat.parse_source(io)).to eq(%{puts "Hello World!"\n})
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#parse_content' do
|
142
|
+
it 'can parse tags' do
|
143
|
+
content, tags = chat.parse_content("see #foobar …", [])
|
144
|
+
expect(content).to eq 'see #foobar …'
|
145
|
+
expect(tags).to include('foobar')
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'can parse https URLs' do
|
149
|
+
stub_request(:get, "https://www.example.com/foo.html").
|
150
|
+
with(headers: { 'Host' => 'www.example.com' }).
|
151
|
+
to_return(
|
152
|
+
status: 200,
|
153
|
+
body: "",
|
154
|
+
headers: { 'Content-Type' => 'text/html' }
|
155
|
+
)
|
156
|
+
content, = chat.parse_content('https://www.example.com/foo.html', [])
|
157
|
+
expect(content).to include 'Imported "https://www.example.com/foo.html"'
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'can parse file URLs' do
|
161
|
+
content, = chat.parse_content("see file://#{Dir.pwd}/spec/assets/example.html", [])
|
162
|
+
expect(content).to include(<<~EOT)
|
163
|
+
Imported "#{Pathname.pwd.join('spec/assets/example.html')}":
|
164
|
+
|
165
|
+
# My First Heading
|
166
|
+
|
167
|
+
My first paragraph.
|
168
|
+
EOT
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'can parse file paths' do
|
172
|
+
content, = chat.parse_content("see #{Dir.pwd}/spec/assets/example.html", [])
|
173
|
+
expect(content).to include(<<~EOT)
|
174
|
+
Imported "#{Pathname.pwd.join('spec/assets/example.html')}":
|
175
|
+
|
176
|
+
# My First Heading
|
177
|
+
|
178
|
+
My first paragraph.
|
179
|
+
EOT
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'can add images' do
|
183
|
+
images = []
|
184
|
+
expect(chat).to receive(:add_image).
|
185
|
+
with(images, kind_of(IO), %r(/spec/assets/kitten.jpg\z)).
|
186
|
+
and_call_original
|
187
|
+
chat.parse_content('./spec/assets/kitten.jpg', images)
|
188
|
+
expect(images.size).to eq 1
|
189
|
+
expect(images.first).to be_a Ollama::Image
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'document_policy' do
|
193
|
+
it 'can be ignoring' do
|
194
|
+
chat.document_policy = 'ignoring'
|
195
|
+
c = "see #{Dir.pwd}/spec/assets/example.html"
|
196
|
+
content, = chat.parse_content(c, [])
|
197
|
+
expect(content).to eq(c)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'can be importing' do
|
201
|
+
chat.document_policy = 'importing'
|
202
|
+
c = "see #{Dir.pwd}/spec/assets/example.html"
|
203
|
+
content, = chat.parse_content(c, [])
|
204
|
+
expect(content).to include(<<~EOT)
|
205
|
+
Imported "#{Pathname.pwd.join('spec/assets/example.html')}":
|
206
|
+
|
207
|
+
# My First Heading
|
208
|
+
|
209
|
+
My first paragraph.
|
210
|
+
EOT
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'can be embedding' do
|
214
|
+
chat.document_policy = 'embedding'
|
215
|
+
c = "see #{Dir.pwd}/spec/assets/example.html"
|
216
|
+
expect(chat).to receive(:embed_source).with(
|
217
|
+
kind_of(IO),
|
218
|
+
Pathname.pwd.join('spec/assets/example.html').to_s
|
219
|
+
)
|
220
|
+
content, = chat.parse_content(c, [])
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'can be summarizing' do
|
224
|
+
chat.document_policy = 'summarizing'
|
225
|
+
c = "see #{Dir.pwd}/spec/assets/example.html"
|
226
|
+
content, = chat.parse_content(c, [])
|
227
|
+
expect(content).to start_with(<<~EOT)
|
228
|
+
see #{Pathname.pwd.join('spec/assets/example.html')}
|
229
|
+
|
230
|
+
Generate an abstract summary of the content in this document using
|
231
|
+
100 words:
|
232
|
+
|
233
|
+
# My First Heading
|
234
|
+
|
235
|
+
My first paragraph.
|
236
|
+
EOT
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OllamaChat::SourceFetching do
|
4
|
+
let :chat do
|
5
|
+
OllamaChat::Chat.new
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_request(:get, %r(/api/tags\z)).
|
10
|
+
to_return(status: 200, body: asset_json('api_tags.json'))
|
11
|
+
stub_request(:post, %r(/api/show\z)).
|
12
|
+
to_return(status: 200, body: asset_json('api_show.json'))
|
13
|
+
allow(chat).to receive(:location).and_return(double(on?: false))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can import' do
|
17
|
+
expect(chat.import('./spec/assets/example.html')).to start_with(<<~EOT)
|
18
|
+
Imported "./spec/assets/example.html":
|
19
|
+
|
20
|
+
# My First Heading
|
21
|
+
|
22
|
+
My first paragraph.
|
23
|
+
EOT
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can summarize' do
|
27
|
+
expect(chat.summarize('./spec/assets/example.html')).to start_with(<<~EOT)
|
28
|
+
Generate an abstract summary of the content in this document using
|
29
|
+
100 words:
|
30
|
+
|
31
|
+
# My First Heading
|
32
|
+
|
33
|
+
My first paragraph.
|
34
|
+
EOT
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can embed' do
|
38
|
+
expect(chat).to receive(:fetch_source).with(
|
39
|
+
'./spec/assets/example.html'
|
40
|
+
)
|
41
|
+
expect(chat.embed('./spec/assets/example.html')).to eq(
|
42
|
+
'This source was now embedded: ./spec/assets/example.html'
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'can search web' do
|
47
|
+
stub_request(:get, "https://www.duckduckgo.com/html/?q=foo").
|
48
|
+
with(headers: { 'Host'=>'www.duckduckgo.com' }).
|
49
|
+
to_return(status: 200, body: asset_content('duckduckgo.html'), headers: {})
|
50
|
+
expect(chat.search_web('foo').first.to_s).to eq(
|
51
|
+
'https://en.wikipedia.org/wiki/Foo_Fighters'
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OllamaChat::Switches do
|
4
|
+
describe OllamaChat::Switches::Switch do
|
5
|
+
let :switch do
|
6
|
+
described_class.new(
|
7
|
+
:test,
|
8
|
+
config: config,
|
9
|
+
msg: {
|
10
|
+
true => "Enabled.",
|
11
|
+
false => "Disabled.",
|
12
|
+
}
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'default to false' do
|
17
|
+
let :config do
|
18
|
+
double(test?: false)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
it 'can be switched on' do
|
23
|
+
expect {
|
24
|
+
switch.set(true)
|
25
|
+
}.to change {
|
26
|
+
switch.on? && !switch.off?
|
27
|
+
}.from(false).to(true)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can be toggled on' do
|
31
|
+
expect(STDOUT).to receive(:puts).with('Enabled.')
|
32
|
+
expect {
|
33
|
+
switch.toggle
|
34
|
+
}.to change {
|
35
|
+
switch.on? && !switch.off?
|
36
|
+
}.from(false).to(true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'default to false' do
|
41
|
+
let :config do
|
42
|
+
double(test?: true)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'can be switched on' do
|
46
|
+
expect {
|
47
|
+
switch.set(false)
|
48
|
+
}.to change {
|
49
|
+
switch.on? && !switch.off?
|
50
|
+
}.from(true).to(false)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can be toggled off' do
|
54
|
+
expect(STDOUT).to receive(:puts).with('Disabled.')
|
55
|
+
expect {
|
56
|
+
switch.toggle
|
57
|
+
}.to change {
|
58
|
+
switch.on? && !switch.off?
|
59
|
+
}.from(true).to(false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe OllamaChat::Switches::CombinedSwitch do
|
65
|
+
describe 'off' do
|
66
|
+
let :config do
|
67
|
+
double(test1?: true, test2?: false)
|
68
|
+
end
|
69
|
+
|
70
|
+
let :switch1 do
|
71
|
+
OllamaChat::Switches::Switch.new(
|
72
|
+
:test1,
|
73
|
+
config: config,
|
74
|
+
msg: {
|
75
|
+
true => "Enabled.",
|
76
|
+
false => "Disabled.",
|
77
|
+
}
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
let :switch2 do
|
82
|
+
OllamaChat::Switches::Switch.new(
|
83
|
+
:test2,
|
84
|
+
config: config,
|
85
|
+
msg: {
|
86
|
+
true => "Enabled.",
|
87
|
+
false => "Disabled.",
|
88
|
+
}
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
let :switch do
|
93
|
+
described_class.new(
|
94
|
+
value: -> { switch1.on? && switch2.off? },
|
95
|
+
msg: {
|
96
|
+
true => "Enabled.",
|
97
|
+
false => "Disabled.",
|
98
|
+
}
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'can be switched off 2' do
|
103
|
+
expect {
|
104
|
+
switch2.set(true)
|
105
|
+
}.to change {
|
106
|
+
switch.on? && !switch.off?
|
107
|
+
}.from(true).to(false)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'can be switched off 1' do
|
111
|
+
expect {
|
112
|
+
switch1.set(false)
|
113
|
+
}.to change {
|
114
|
+
switch.on? && !switch.off?
|
115
|
+
}.from(true).to(false)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'on' do
|
120
|
+
let :config do
|
121
|
+
double(test1?: false, test2?: true)
|
122
|
+
end
|
123
|
+
|
124
|
+
let :switch1 do
|
125
|
+
OllamaChat::Switches::Switch.new(
|
126
|
+
:test1,
|
127
|
+
config: config,
|
128
|
+
msg: {
|
129
|
+
true => "Enabled.",
|
130
|
+
false => "Disabled.",
|
131
|
+
}
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
let :switch2 do
|
136
|
+
OllamaChat::Switches::Switch.new(
|
137
|
+
:test2,
|
138
|
+
config: config,
|
139
|
+
msg: {
|
140
|
+
true => "Enabled.",
|
141
|
+
false => "Disabled.",
|
142
|
+
}
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
let :switch do
|
147
|
+
described_class.new(
|
148
|
+
value: -> { switch1.on? && switch2.off? },
|
149
|
+
msg: {
|
150
|
+
true => "Enabled.",
|
151
|
+
false => "Disabled.",
|
152
|
+
}
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'can be switched on' do
|
157
|
+
switch
|
158
|
+
expect {
|
159
|
+
switch1.set(true)
|
160
|
+
switch2.set(false)
|
161
|
+
}.to change {
|
162
|
+
switch.on? && !switch.off?
|
163
|
+
}.from(false).to(true)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OllamaChat::Utils::CacheFetcher do
|
4
|
+
let :url do
|
5
|
+
'https://www.example.com/hello'
|
6
|
+
end
|
7
|
+
|
8
|
+
let :cache do
|
9
|
+
double('RedisCache')
|
10
|
+
end
|
11
|
+
|
12
|
+
let :fetcher do
|
13
|
+
described_class.new(cache).expose
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can be instantiated' do
|
17
|
+
expect(fetcher).to be_a described_class
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'has #get' do
|
21
|
+
expect(cache).to receive(:[]).with('body-69ce405ab83f42dffa9fd22bbd47783f').and_return 'world'
|
22
|
+
expect(cache).to receive(:[]).with('content_type-69ce405ab83f42dffa9fd22bbd47783f').and_return 'text/plain'
|
23
|
+
yielded_io = nil
|
24
|
+
block = -> io { yielded_io = io }
|
25
|
+
fetcher.get(url, &block)
|
26
|
+
expect(yielded_io).to be_a StringIO
|
27
|
+
expect(yielded_io.read).to eq 'world'
|
28
|
+
end
|
29
|
+
|
30
|
+
it '#get needs block' do
|
31
|
+
expect { fetcher.get(url) }.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has #put' do
|
35
|
+
io = StringIO.new('world')
|
36
|
+
io.extend(OllamaChat::Utils::Fetcher::HeaderExtension)
|
37
|
+
io.content_type = MIME::Types['text/plain'].first
|
38
|
+
io.ex = 666
|
39
|
+
expect(cache).to receive(:set).with('body-69ce405ab83f42dffa9fd22bbd47783f', 'world', ex: 666)
|
40
|
+
expect(cache).to receive(:set).with('content_type-69ce405ab83f42dffa9fd22bbd47783f', 'text/plain', ex: 666)
|
41
|
+
fetcher.put(url, io)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OllamaChat::Utils::Fetcher do
|
4
|
+
let :url do
|
5
|
+
'https://www.example.com/hello'
|
6
|
+
end
|
7
|
+
|
8
|
+
let :fetcher do
|
9
|
+
described_class.new.expose
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can be instantiated' do
|
13
|
+
expect(fetcher).to be_a described_class
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has .get' do
|
17
|
+
expect(described_class).to receive(:new).and_return double(get: true)
|
18
|
+
described_class.get(url)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can #get with streaming' do
|
22
|
+
stub_request(:get, url).
|
23
|
+
with(headers: fetcher.headers).
|
24
|
+
to_return(
|
25
|
+
status: 200,
|
26
|
+
body: 'world',
|
27
|
+
headers: { 'Content-Type' => 'text/plain' },
|
28
|
+
)
|
29
|
+
fetcher.get(url) do |tmp|
|
30
|
+
expect(tmp).to be_a Tempfile
|
31
|
+
expect(tmp.read).to eq 'world'
|
32
|
+
expect(tmp.content_type).to eq 'text/plain'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can #get without ssl peer verification' do
|
37
|
+
fetcher = described_class.new(
|
38
|
+
http_options: { ssl_verify_peer: false }
|
39
|
+
).expose
|
40
|
+
stub_request(:get, url).
|
41
|
+
with(headers: fetcher.headers).
|
42
|
+
to_return(
|
43
|
+
status: 200,
|
44
|
+
body: 'world',
|
45
|
+
headers: { 'Content-Type' => 'text/plain' },
|
46
|
+
)
|
47
|
+
expect(Excon).to receive(:new).with(
|
48
|
+
url,
|
49
|
+
hash_including(ssl_verify_peer: false)
|
50
|
+
).and_call_original
|
51
|
+
fetcher.get(url) do |tmp|
|
52
|
+
expect(tmp).to be_a Tempfile
|
53
|
+
expect(tmp.read).to eq 'world'
|
54
|
+
expect(tmp.content_type).to eq 'text/plain'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'can #get and fallback from streaming' do
|
59
|
+
stub_request(:get, url).
|
60
|
+
with(headers: fetcher.headers).
|
61
|
+
to_return(
|
62
|
+
{ status: 501 },
|
63
|
+
{
|
64
|
+
status: 200,
|
65
|
+
body: 'world',
|
66
|
+
headers: { 'Content-Type' => 'text/plain' },
|
67
|
+
}
|
68
|
+
)
|
69
|
+
fetcher.get(url) do |tmp|
|
70
|
+
expect(tmp).to be_a Tempfile
|
71
|
+
expect(tmp.read).to eq 'world'
|
72
|
+
expect(tmp.content_type).to eq 'text/plain'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'can #get and finally fail' do
|
77
|
+
stub_request(:get, url).
|
78
|
+
with(headers: fetcher.headers).
|
79
|
+
to_return(status: 500)
|
80
|
+
expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
|
81
|
+
fetcher.get(url) do |tmp|
|
82
|
+
expect(tmp).to be_a StringIO
|
83
|
+
expect(tmp.read).to eq ''
|
84
|
+
expect(tmp.content_type).to eq 'text/plain'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can redirect' do
|
89
|
+
expect(fetcher.middlewares).to include Excon::Middleware::RedirectFollower
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'can .read' do
|
93
|
+
described_class.read(__FILE__) do |file|
|
94
|
+
expect(file).to be_a File
|
95
|
+
expect(file.read).to include 'can .read'
|
96
|
+
expect(file.content_type).to eq 'application/x-ruby'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'can .execute' do
|
101
|
+
described_class.execute('echo -n hello world') do |file|
|
102
|
+
expect(file).to be_a Tempfile
|
103
|
+
expect(file.read).to eq 'hello world'
|
104
|
+
expect(file.content_type).to eq 'text/plain'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'can .execute and fail' do
|
109
|
+
expect(IO).to receive(:popen).and_raise StandardError
|
110
|
+
expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
|
111
|
+
described_class.execute('foobar') do |file|
|
112
|
+
expect(file).to be_a StringIO
|
113
|
+
expect(file.read).to be_empty
|
114
|
+
expect(file.content_type).to eq 'text/plain'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '.normalize_url' do
|
119
|
+
it 'can handle umlauts' do
|
120
|
+
expect(described_class.normalize_url('https://foo.de/bär')).to eq(
|
121
|
+
'https://foo.de/b%C3%A4r'
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'can handle escaped umlauts' do
|
126
|
+
expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
|
127
|
+
'https://foo.de/b%C3%A4r'
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'can remove #anchors' do
|
132
|
+
expect(described_class.normalize_url('https://foo.de#bar')).to eq(
|
133
|
+
'https://foo.de'
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OllamaChat::Utils::FileArgument do
|
4
|
+
it 'it can return content' do
|
5
|
+
expect(described_class.get_file_argument('foo')).to eq 'foo'
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'it can return content at path' do
|
9
|
+
expect(described_class.get_file_argument(asset('prompt.txt'))).to include\
|
10
|
+
'test prompt'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'it can return default content' do
|
14
|
+
expect(described_class.get_file_argument('', default: 'foo')).to eq 'foo'
|
15
|
+
expect(described_class.get_file_argument(nil, default: 'foo')).to eq 'foo'
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
if ENV['START_SIMPLECOV'].to_i == 1
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter "#{File.basename(File.dirname(__FILE__))}/"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
require 'rspec'
|
8
|
+
require 'tins/xt/expose'
|
9
|
+
begin
|
10
|
+
require 'debug'
|
11
|
+
rescue LoadError
|
12
|
+
end
|
13
|
+
require 'webmock/rspec'
|
14
|
+
WebMock.disable_net_connect!
|
15
|
+
require 'ollama_chat'
|
16
|
+
|
17
|
+
def asset(name)
|
18
|
+
File.join(__dir__, 'assets', name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def asset_content(name)
|
22
|
+
File.read(File.join(__dir__, 'assets', name))
|
23
|
+
end
|
24
|
+
|
25
|
+
def asset_io(name, &block)
|
26
|
+
io = File.new(File.join(__dir__, 'assets', name))
|
27
|
+
if block
|
28
|
+
begin
|
29
|
+
block.call(io)
|
30
|
+
ensure
|
31
|
+
io.close
|
32
|
+
end
|
33
|
+
else
|
34
|
+
io
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def asset_json(name)
|
39
|
+
JSON(JSON(File.read(asset(name))))
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec.configure do |config|
|
43
|
+
config.before(:suite) do
|
44
|
+
infobar.show = nil
|
45
|
+
end
|
46
|
+
end
|
data/tmp/.keep
ADDED
File without changes
|