ollama-ruby 0.0.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 +7 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +430 -0
- data/Rakefile +35 -0
- data/bin/ollama_chat +258 -0
- data/bin/ollama_console +20 -0
- data/lib/ollama/client/command.rb +25 -0
- data/lib/ollama/client/doc.rb +26 -0
- data/lib/ollama/client.rb +137 -0
- data/lib/ollama/commands/chat.rb +21 -0
- data/lib/ollama/commands/copy.rb +19 -0
- data/lib/ollama/commands/create.rb +20 -0
- data/lib/ollama/commands/delete.rb +19 -0
- data/lib/ollama/commands/embed.rb +21 -0
- data/lib/ollama/commands/embeddings.rb +20 -0
- data/lib/ollama/commands/generate.rb +21 -0
- data/lib/ollama/commands/ps.rb +19 -0
- data/lib/ollama/commands/pull.rb +19 -0
- data/lib/ollama/commands/push.rb +19 -0
- data/lib/ollama/commands/show.rb +20 -0
- data/lib/ollama/commands/tags.rb +19 -0
- data/lib/ollama/dto.rb +42 -0
- data/lib/ollama/errors.rb +15 -0
- data/lib/ollama/handlers/collector.rb +17 -0
- data/lib/ollama/handlers/concern.rb +31 -0
- data/lib/ollama/handlers/dump_json.rb +8 -0
- data/lib/ollama/handlers/dump_yaml.rb +8 -0
- data/lib/ollama/handlers/markdown.rb +22 -0
- data/lib/ollama/handlers/nop.rb +7 -0
- data/lib/ollama/handlers/print.rb +16 -0
- data/lib/ollama/handlers/progress.rb +36 -0
- data/lib/ollama/handlers/say.rb +19 -0
- data/lib/ollama/handlers/single.rb +17 -0
- data/lib/ollama/handlers.rb +13 -0
- data/lib/ollama/image.rb +31 -0
- data/lib/ollama/message.rb +9 -0
- data/lib/ollama/options.rb +68 -0
- data/lib/ollama/response.rb +5 -0
- data/lib/ollama/tool/function/parameters/property.rb +9 -0
- data/lib/ollama/tool/function/parameters.rb +10 -0
- data/lib/ollama/tool/function.rb +11 -0
- data/lib/ollama/tool.rb +9 -0
- data/lib/ollama/utils/ansi_markdown.rb +217 -0
- data/lib/ollama/utils/width.rb +22 -0
- data/lib/ollama/version.rb +8 -0
- data/lib/ollama.rb +43 -0
- data/ollama-ruby.gemspec +36 -0
- data/spec/assets/kitten.jpg +0 -0
- data/spec/ollama/client/doc_spec.rb +11 -0
- data/spec/ollama/client_spec.rb +144 -0
- data/spec/ollama/commands/chat_spec.rb +52 -0
- data/spec/ollama/commands/copy_spec.rb +28 -0
- data/spec/ollama/commands/create_spec.rb +37 -0
- data/spec/ollama/commands/delete_spec.rb +28 -0
- data/spec/ollama/commands/embed_spec.rb +52 -0
- data/spec/ollama/commands/embeddings_spec.rb +38 -0
- data/spec/ollama/commands/generate_spec.rb +29 -0
- data/spec/ollama/commands/ps_spec.rb +25 -0
- data/spec/ollama/commands/pull_spec.rb +28 -0
- data/spec/ollama/commands/push_spec.rb +28 -0
- data/spec/ollama/commands/show_spec.rb +28 -0
- data/spec/ollama/commands/tags_spec.rb +22 -0
- data/spec/ollama/handlers/collector_spec.rb +15 -0
- data/spec/ollama/handlers/dump_json_spec.rb +16 -0
- data/spec/ollama/handlers/dump_yaml_spec.rb +18 -0
- data/spec/ollama/handlers/markdown_spec.rb +46 -0
- data/spec/ollama/handlers/nop_spec.rb +15 -0
- data/spec/ollama/handlers/print_spec.rb +30 -0
- data/spec/ollama/handlers/progress_spec.rb +22 -0
- data/spec/ollama/handlers/say_spec.rb +30 -0
- data/spec/ollama/handlers/single_spec.rb +24 -0
- data/spec/ollama/image_spec.rb +23 -0
- data/spec/ollama/message_spec.rb +37 -0
- data/spec/ollama/options_spec.rb +25 -0
- data/spec/ollama/tool_spec.rb +78 -0
- data/spec/ollama/utils/ansi_markdown_spec.rb +15 -0
- data/spec/spec_helper.rb +16 -0
- metadata +321 -0
data/bin/ollama_chat
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ollama'
|
4
|
+
include Ollama
|
5
|
+
require 'term/ansicolor'
|
6
|
+
include Term::ANSIColor
|
7
|
+
require 'tins/go'
|
8
|
+
include Tins::GO
|
9
|
+
require 'reline'
|
10
|
+
|
11
|
+
class FollowChat
|
12
|
+
include Ollama::Handlers::Concern
|
13
|
+
include Term::ANSIColor
|
14
|
+
|
15
|
+
def initialize(messages:, markdown: false, voice: nil, output: $stdout)
|
16
|
+
super(output:)
|
17
|
+
@output.sync = true
|
18
|
+
@markdown = markdown
|
19
|
+
@say = voice ? Ollama::Handlers::Say.new(voice:) : NOP
|
20
|
+
@messages = messages
|
21
|
+
@user = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(response)
|
25
|
+
ENV['DEBUG'].to_i == 1 and jj response
|
26
|
+
if response&.message&.role == 'assistant'
|
27
|
+
if @messages.last.role != 'assistant'
|
28
|
+
@messages << Ollama::Message.new(role: 'assistant', content: '')
|
29
|
+
@user = message_type(@messages.last.images) + " " +
|
30
|
+
bold { color(111) { 'assistant:' } }
|
31
|
+
puts @user unless @markdown
|
32
|
+
end
|
33
|
+
content = response.message&.content
|
34
|
+
@messages.last.content << content
|
35
|
+
if @markdown and @messages.last.content.present?
|
36
|
+
markdown_content = Ollama::Utils::ANSIMarkdown.parse(@messages.last.content)
|
37
|
+
@output.print clear_screen, move_home, @user, ?\n, markdown_content
|
38
|
+
else
|
39
|
+
@output.print content
|
40
|
+
end
|
41
|
+
@say.call(response)
|
42
|
+
end
|
43
|
+
response.done and @output.puts
|
44
|
+
self
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def pull_model_unless_present(client, model, options)
|
49
|
+
retried = false
|
50
|
+
begin
|
51
|
+
client.show(name: model) { |response|
|
52
|
+
puts green {
|
53
|
+
"Model with architecture #{response.model_info['general.architecture']} found."
|
54
|
+
}
|
55
|
+
if options
|
56
|
+
puts "Model options are:"
|
57
|
+
jj options
|
58
|
+
end
|
59
|
+
if system = response.system
|
60
|
+
puts "Configured model system prompt is:\n#{italic { system }}"
|
61
|
+
return system
|
62
|
+
else
|
63
|
+
return
|
64
|
+
end
|
65
|
+
}
|
66
|
+
rescue Errors::NotFoundError
|
67
|
+
puts "Model #{model} not found, attempting to pull it now…"
|
68
|
+
client.pull(name: model)
|
69
|
+
if retried
|
70
|
+
exit 1
|
71
|
+
else
|
72
|
+
retried = true
|
73
|
+
retry
|
74
|
+
end
|
75
|
+
rescue Errors::Error => e
|
76
|
+
warn "Caught #{e.class}: #{e} => Exiting."
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_conversation(filename)
|
82
|
+
unless File.exist?(filename)
|
83
|
+
puts "File #{filename} doesn't exist. Choose another filename."
|
84
|
+
return
|
85
|
+
end
|
86
|
+
File.open(filename, 'r') do |output|
|
87
|
+
return JSON(output.read, create_additions: true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def save_conversation(filename, messages)
|
92
|
+
if File.exist?(filename)
|
93
|
+
puts "File #{filename} already exists. Choose another filename."
|
94
|
+
return
|
95
|
+
end
|
96
|
+
File.open(filename, 'w') do |output|
|
97
|
+
output.puts JSON(messages)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def message_type(images)
|
102
|
+
if images.present?
|
103
|
+
?📸
|
104
|
+
else
|
105
|
+
?📨
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def list_conversation(messages, markdown)
|
110
|
+
messages.each do |m|
|
111
|
+
role_color = case m.role
|
112
|
+
when 'user' then 172
|
113
|
+
when 'assistant' then 111
|
114
|
+
when 'system' then 213
|
115
|
+
else 210
|
116
|
+
end
|
117
|
+
content = if markdown && m.content.present?
|
118
|
+
Ollama::Utils::ANSIMarkdown.parse(m.content)
|
119
|
+
else
|
120
|
+
m.content
|
121
|
+
end
|
122
|
+
puts message_type(m.images) + " " +
|
123
|
+
bold { color(role_color) { m.role } } + ":\n#{content}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def display_chat_help
|
128
|
+
puts <<~end
|
129
|
+
/paste to paste content
|
130
|
+
/list list the messages of the conversation
|
131
|
+
/clear clear the conversation messages
|
132
|
+
/pop n pop the last n message, defaults to 1
|
133
|
+
/regenerate the last answer message
|
134
|
+
/save filename store conversation messages
|
135
|
+
/load filename load conversation messages
|
136
|
+
/image filename attach image to the next message
|
137
|
+
/quit to quit.
|
138
|
+
/help to view this help.
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def usage
|
143
|
+
puts <<~end
|
144
|
+
#{File.basename($0)} [OPTIONS]
|
145
|
+
|
146
|
+
-u URL the ollama base url, OLLAMA_URL
|
147
|
+
-m MODEL the ollama model to chat with, OLLAMA_MODEL
|
148
|
+
-M OPTIONS the model options as JSON file, see Ollama::Options
|
149
|
+
-s SYSTEM the system prompt to use as a file
|
150
|
+
-c CHAT a saved chat conversation to load
|
151
|
+
-v VOICE use VOICE (e. g. Samantha) to speak with say command
|
152
|
+
-d use markdown to display the chat messages
|
153
|
+
-h this help
|
154
|
+
|
155
|
+
end
|
156
|
+
exit 0
|
157
|
+
end
|
158
|
+
|
159
|
+
opts = go 'u:m:M:s:c:v:dh'
|
160
|
+
|
161
|
+
opts[?h] and usage
|
162
|
+
|
163
|
+
base_url = opts[?u] || ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
|
164
|
+
model = opts[?m] || ENV.fetch('OLLAMA_MODEL', 'llama3.1')
|
165
|
+
options = if options_file = opts[?M]
|
166
|
+
JSON(File.read(options_file), create_additions: true)
|
167
|
+
end
|
168
|
+
|
169
|
+
client = Client.new(base_url:)
|
170
|
+
|
171
|
+
model_system = pull_model_unless_present(client, model, options)
|
172
|
+
|
173
|
+
puts green { "Connecting to #{model}@#{base_url} now…" }
|
174
|
+
|
175
|
+
messages = []
|
176
|
+
|
177
|
+
if opts[?c]
|
178
|
+
messages.concat load_conversation(opts[?c])
|
179
|
+
else
|
180
|
+
system = nil
|
181
|
+
if system_prompt_file = opts[?s]
|
182
|
+
system = File.read(system_prompt_file)
|
183
|
+
end
|
184
|
+
system ||= ENV['OLLAMA_SYSTEM']
|
185
|
+
|
186
|
+
if system
|
187
|
+
messages << Message.new(role: 'system', content: system)
|
188
|
+
puts "Configured system prompt is:\n#{italic { system }}"
|
189
|
+
elsif model_system.present?
|
190
|
+
puts "Using model system prompt."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
puts "Type /help to display the chat help."
|
195
|
+
|
196
|
+
images = nil
|
197
|
+
loop do
|
198
|
+
prompt = bold { color(172) { message_type(images) + " user" } } + bold { "> " }
|
199
|
+
case content = Reline.readline(prompt, true)&.chomp
|
200
|
+
when %r(^/paste$)
|
201
|
+
puts bold { "Paste your content and then press C-d!" }
|
202
|
+
content = STDIN.read
|
203
|
+
when %r(^/quit$)
|
204
|
+
puts "Goodbye."
|
205
|
+
exit 0
|
206
|
+
when %r(^/list$)
|
207
|
+
list_conversation(messages, opts[?d])
|
208
|
+
next
|
209
|
+
when %r(^/clear$)
|
210
|
+
messages.clear
|
211
|
+
puts "Cleared messages."
|
212
|
+
next
|
213
|
+
when %r(^/pop\s*(\d*)$)
|
214
|
+
n = $1.to_i.clamp(1, Float::INFINITY)
|
215
|
+
messages.pop(n)
|
216
|
+
puts "Popped the last #{n} messages."
|
217
|
+
next
|
218
|
+
when %r(^/regenerate$)
|
219
|
+
if content = messages[-2]&.content
|
220
|
+
images = messages[-2]&.images
|
221
|
+
messages.pop(2)
|
222
|
+
else
|
223
|
+
puts "Not enough messages in this conversation."
|
224
|
+
redo
|
225
|
+
end
|
226
|
+
when %r(^/save (.+)$)
|
227
|
+
save_conversation($1, messages)
|
228
|
+
puts "Saved conversation to #$1."
|
229
|
+
next
|
230
|
+
when %r(^/load (.+)$)
|
231
|
+
messages = load_conversation($1)
|
232
|
+
puts "Loaded conversation from #$1."
|
233
|
+
next
|
234
|
+
when %r(^/image (.+)$)
|
235
|
+
filename = File.expand_path($1)
|
236
|
+
if File.exist?(filename)
|
237
|
+
images = Image.for_filename(filename)
|
238
|
+
puts "Attached image #$1 to the next message."
|
239
|
+
redo
|
240
|
+
else
|
241
|
+
puts "Filename #$1 doesn't exist. Choose another one."
|
242
|
+
next
|
243
|
+
end
|
244
|
+
when %r(^/help$)
|
245
|
+
display_chat_help
|
246
|
+
next
|
247
|
+
when nil
|
248
|
+
puts "Type /quit to quit."
|
249
|
+
next
|
250
|
+
end
|
251
|
+
messages << Message.new(role: 'user', content:, images:)
|
252
|
+
handler = FollowChat.new(messages:, markdown: opts[?d], voice: opts[?v])
|
253
|
+
client.chat(model:, messages:, options:, stream: true, &handler)
|
254
|
+
ENV['DEBUG'].to_i == 1 and jj messages
|
255
|
+
images = nil
|
256
|
+
rescue Interrupt
|
257
|
+
puts "Type /quit to quit."
|
258
|
+
end
|
data/bin/ollama_console
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ollama'
|
4
|
+
include Ollama
|
5
|
+
require 'irb'
|
6
|
+
require 'irb/history'
|
7
|
+
|
8
|
+
base_url = ENV['OLLAMA_URL'] || 'http://%s' % ENV.fetch('OLLAMA_HOST')
|
9
|
+
client = Client.new(base_url:)
|
10
|
+
IRB.setup nil
|
11
|
+
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
|
12
|
+
IRB.conf[:HISTORY_FILE] = File.join(ENV.fetch('HOME'), '.ollama_console-history')
|
13
|
+
IRB.conf[:SAVE_HISTORY] = 1000
|
14
|
+
require 'irb/ext/multi-irb'
|
15
|
+
if io = IRB.conf[:MAIN_CONTEXT].io and io.support_history_saving?
|
16
|
+
io.load_history
|
17
|
+
at_exit { io.save_history }
|
18
|
+
end
|
19
|
+
client.help
|
20
|
+
IRB.irb nil, client
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ollama::Client::Command
|
2
|
+
extend Tins::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
# Create Command +name+, if +stream+ was true, set stream_handler as
|
6
|
+
# default, otherwise default_handler.
|
7
|
+
def command(name, default_handler:, stream_handler: nil)
|
8
|
+
klass = Ollama::Commands.const_get(name.to_s.camelize)
|
9
|
+
doc Ollama::Client::Doc.new(name)
|
10
|
+
define_method(name) do |**parameters, &handler|
|
11
|
+
instance = klass.new(**parameters)
|
12
|
+
instance.client = self
|
13
|
+
unless handler
|
14
|
+
instance.stream and stream_handler and
|
15
|
+
handler ||= stream_handler
|
16
|
+
handler ||= default_handler
|
17
|
+
end
|
18
|
+
handler.is_a?(Class) and handler = handler.new
|
19
|
+
instance.perform(handler)
|
20
|
+
handler.result if handler.respond_to?(:result)
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
class Ollama::Client::Doc
|
4
|
+
include Term::ANSIColor
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@url = Hash.new('https://github.com/ollama/ollama/blob/main/docs/api.md').merge(
|
9
|
+
generate: 'https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-completion',
|
10
|
+
chat: 'https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion',
|
11
|
+
create: 'https://github.com/ollama/ollama/blob/main/docs/api.md#create-a-model',
|
12
|
+
tags: 'https://github.com/ollama/ollama/blob/main/docs/api.md#list-local-models',
|
13
|
+
show: 'https://github.com/ollama/ollama/blob/main/docs/api.md#show-model-information',
|
14
|
+
copy: 'https://github.com/ollama/ollama/blob/main/docs/api.md#copy-a-model',
|
15
|
+
delete: 'https://github.com/ollama/ollama/blob/main/docs/api.md#delete-a-model',
|
16
|
+
pull: 'https://github.com/ollama/ollama/blob/main/docs/api.md#pull-a-model',
|
17
|
+
push: 'https://github.com/ollama/ollama/blob/main/docs/api.md#push-a-model',
|
18
|
+
embeddings: 'https://github.com/ollama/ollama/blob/main/docs/api.md#generate-embeddings',
|
19
|
+
ps: 'https://github.com/ollama/ollama/blob/main/docs/api.md#list-running-models',
|
20
|
+
)[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
(hyperlink(@url) { @name } if @url).to_s
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'tins/xt/string_camelize'
|
2
|
+
require 'tins/annotate'
|
3
|
+
require 'excon'
|
4
|
+
|
5
|
+
class Ollama::Client
|
6
|
+
end
|
7
|
+
require 'ollama/client/doc'
|
8
|
+
require 'ollama/client/command'
|
9
|
+
|
10
|
+
class Ollama::Client
|
11
|
+
include Tins::Annotate
|
12
|
+
include Ollama::Handlers
|
13
|
+
include Ollama::Client::Command
|
14
|
+
|
15
|
+
annotate :doc
|
16
|
+
|
17
|
+
def initialize(base_url: nil, output: $stdout, connect_timeout: nil, read_timeout: nil, write_timeout: nil, debug: nil)
|
18
|
+
base_url.nil? and base_url = ENV.fetch('OLLAMA_URL') do
|
19
|
+
raise ArgumentError,
|
20
|
+
'missing :base_url parameter or OLLAMA_URL environment variable'
|
21
|
+
end
|
22
|
+
base_url.is_a? URI or base_url = URI.parse(base_url)
|
23
|
+
base_url.is_a?(URI::HTTP) || base_url.is_a?(URI::HTTPS) or
|
24
|
+
raise ArgumentError, "require #{base_url.inspect} to be http/https-URI"
|
25
|
+
@ssl_verify_peer = base_url.query.to_s.split(?&).inject({}) { |h, l|
|
26
|
+
h.merge Hash[*l.split(?=)]
|
27
|
+
}['ssl_verify_peer'] != 'false'
|
28
|
+
@base_url, @output, @connect_timeout, @read_timeout, @write_timeout, @debug =
|
29
|
+
base_url, output, connect_timeout, read_timeout, write_timeout, debug
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :output
|
33
|
+
|
34
|
+
def ssl_verify_peer?
|
35
|
+
!!@ssl_verify_peer
|
36
|
+
end
|
37
|
+
|
38
|
+
command(:chat, default_handler: Single, stream_handler: Collector)
|
39
|
+
|
40
|
+
command(:generate, default_handler: Single, stream_handler: Collector)
|
41
|
+
|
42
|
+
command(:tags, default_handler: Single)
|
43
|
+
|
44
|
+
command(:show, default_handler: Single)
|
45
|
+
|
46
|
+
command(:create, default_handler: Single, stream_handler: Progress)
|
47
|
+
|
48
|
+
command(:copy, default_handler: Single)
|
49
|
+
|
50
|
+
command(:delete, default_handler: Single)
|
51
|
+
|
52
|
+
command(:pull, default_handler: Single, stream_handler: Progress)
|
53
|
+
|
54
|
+
command(:push, default_handler: Single, stream_handler: Progress)
|
55
|
+
|
56
|
+
command(:embed, default_handler: Single)
|
57
|
+
|
58
|
+
command(:embeddings, default_handler: Single)
|
59
|
+
|
60
|
+
command(:ps, default_handler: Single)
|
61
|
+
|
62
|
+
def commands
|
63
|
+
doc_annotations.sort_by(&:first).transpose.last
|
64
|
+
end
|
65
|
+
|
66
|
+
doc Doc.new(:help)
|
67
|
+
def help
|
68
|
+
@output.puts "Commands: %s" % commands.join(?,)
|
69
|
+
end
|
70
|
+
|
71
|
+
def request(method:, path:, handler:, body: nil, stream: nil)
|
72
|
+
url = @base_url + path
|
73
|
+
responses = Enumerator.new do |yielder|
|
74
|
+
if stream
|
75
|
+
response_block = -> chunk, remaining_bytes, total_bytes do
|
76
|
+
response_line = parse_json(chunk)
|
77
|
+
response_line and yielder.yield response_line
|
78
|
+
end
|
79
|
+
response = excon(url).send(method, headers:, body:, response_block:)
|
80
|
+
else
|
81
|
+
response = excon(url).send(method, headers:, body:)
|
82
|
+
end
|
83
|
+
|
84
|
+
case response.status
|
85
|
+
when 200
|
86
|
+
response.body.each_line do |l|
|
87
|
+
response_line = parse_json(l)
|
88
|
+
response_line and yielder.yield response_line
|
89
|
+
end
|
90
|
+
when 404
|
91
|
+
raise Ollama::Errors::NotFoundError, "#{response.status} #{response.body.inspect}"
|
92
|
+
else
|
93
|
+
raise Ollama::Errors::Error, "#{response.status} #{response.body.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
responses.each { |response| handler.call(response) }
|
97
|
+
self
|
98
|
+
rescue Excon::Errors::SocketError => e
|
99
|
+
raise Ollama::Errors::SocketError, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
|
100
|
+
rescue Excon::Errors::Timeout => e
|
101
|
+
raise Ollama::Errors::TimeoutError, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
|
102
|
+
rescue Excon::Error => e
|
103
|
+
raise Ollama::Errors::Error, "Caught #{e.class} #{e.message.inspect} for #{url.to_s.inspect}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def inspect
|
107
|
+
"#<#{self.class}@#{@base_url.to_s}>"
|
108
|
+
end
|
109
|
+
|
110
|
+
alias to_s inspect
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def headers
|
115
|
+
{
|
116
|
+
'User-Agent' => '%s/%s' % [ self.class, Ollama::VERSION ],
|
117
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def excon(url)
|
122
|
+
params = {
|
123
|
+
connect_timeout: @connect_timeout,
|
124
|
+
read_timeout: @read_timeout,
|
125
|
+
write_timeout: @write_timeout,
|
126
|
+
ssl_verify_peer: @ssl_verify_peer,
|
127
|
+
debug: @debug,
|
128
|
+
}.compact
|
129
|
+
Excon.new(url, params)
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse_json(string)
|
133
|
+
JSON.parse(string, object_class: Ollama::Response)
|
134
|
+
rescue JSON::ParserError
|
135
|
+
return
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Ollama::Commands::Chat
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/chat'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model:, messages:, tools: nil, format: nil, options: nil, stream: nil, keep_alive: nil)
|
9
|
+
@model, @messages, @tools, @format, @options, @stream, @keep_alive =
|
10
|
+
model, as_array_of_hashes(messages), as_array_of_hashes(tools),
|
11
|
+
format, options, stream, keep_alive
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :model, :messages, :tools, :format, :options, :stream, :keep_alive
|
15
|
+
|
16
|
+
attr_writer :client
|
17
|
+
|
18
|
+
def perform(handler)
|
19
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Ollama::Commands::Copy
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/copy'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(source:, destination:)
|
9
|
+
@source, @destination, @stream = source, destination, false
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :source, :destination, :stream
|
13
|
+
|
14
|
+
attr_writer :client
|
15
|
+
|
16
|
+
def perform(handler)
|
17
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Ollama::Commands::Create
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/create'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(name:, modelfile: nil, quantize: nil, stream: nil, path: nil)
|
9
|
+
@name, @modelfile, @quantize, @stream, @path =
|
10
|
+
name, modelfile, quantize, stream, path
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :name, :modelfile, :quantize, :stream, :path
|
14
|
+
|
15
|
+
attr_writer :client
|
16
|
+
|
17
|
+
def perform(handler)
|
18
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Ollama::Commands::Delete
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/delete'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(name:)
|
9
|
+
@name, @stream = name, false
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :name, :stream
|
13
|
+
|
14
|
+
attr_writer :client
|
15
|
+
|
16
|
+
def perform(handler)
|
17
|
+
@client.request(method: :delete, path: self.class.path, body: to_json, stream:, handler:)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Ollama::Commands::Embed
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/embed'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model:, input:, truncate: nil, keep_alive: nil)
|
9
|
+
@model, @input, @truncate, @keep_alive =
|
10
|
+
model, input, truncate, keep_alive
|
11
|
+
@stream = false
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :model, :input, :truncate, :keep_alive, :stream
|
15
|
+
|
16
|
+
attr_writer :client
|
17
|
+
|
18
|
+
def perform(handler)
|
19
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Ollama::Commands::Embeddings
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/embeddings'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model:, prompt:, options: nil, keep_alive: nil)
|
9
|
+
@model, @prompt, @options, @keep_alive, @stream =
|
10
|
+
model, prompt, options, keep_alive, false
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :model, :prompt, :options, :keep_alive, :stream
|
14
|
+
|
15
|
+
attr_writer :client
|
16
|
+
|
17
|
+
def perform(handler)
|
18
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Ollama::Commands::Generate
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/generate'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model:, prompt:, suffix: nil, images: nil, format: nil, options: nil, system: nil, template: nil, context: nil, stream: nil, raw: nil, keep_alive: nil)
|
9
|
+
@model, @prompt, @suffix, @images, @format, @options, @system, @template, @context, @stream, @raw, @keep_alive =
|
10
|
+
model, prompt, suffix, (Array(images) if images), format, options, system, template, context, stream, raw, keep_alive
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :model, :prompt, :suffix, :images, :format, :options, :system,
|
14
|
+
:template, :context, :stream, :raw, :keep_alive
|
15
|
+
|
16
|
+
attr_writer :client
|
17
|
+
|
18
|
+
def perform(handler)
|
19
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Ollama::Commands::Ps
|
2
|
+
def self.path
|
3
|
+
'/api/ps'
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(**parameters)
|
7
|
+
parameters.empty? or raise ArgumentError,
|
8
|
+
"Invalid parameters: #{parameters.keys * ' '}"
|
9
|
+
@stream = false
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :stream
|
13
|
+
|
14
|
+
attr_writer :client
|
15
|
+
|
16
|
+
def perform(handler)
|
17
|
+
@client.request(method: :get, path: self.class.path, stream:, handler:)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Ollama::Commands::Pull
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/pull'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(name:, insecure: nil, stream: true)
|
9
|
+
@name, @insecure, @stream = name, insecure, stream
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :name, :insecure, :stream
|
13
|
+
|
14
|
+
attr_writer :client
|
15
|
+
|
16
|
+
def perform(handler)
|
17
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Ollama::Commands::Push
|
2
|
+
include Ollama::DTO
|
3
|
+
|
4
|
+
def self.path
|
5
|
+
'/api/push'
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(name:, insecure: nil, stream: true)
|
9
|
+
@name, @insecure, @stream = name, insecure, stream
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :name, :insecure, :stream
|
13
|
+
|
14
|
+
attr_writer :client
|
15
|
+
|
16
|
+
def perform(handler)
|
17
|
+
@client.request(method: :post, path: self.class.path, body: to_json, stream:, handler:)
|
18
|
+
end
|
19
|
+
end
|