ollama-ruby 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/LICENSE +19 -0
  4. data/README.md +430 -0
  5. data/Rakefile +35 -0
  6. data/bin/ollama_chat +258 -0
  7. data/bin/ollama_console +20 -0
  8. data/lib/ollama/client/command.rb +25 -0
  9. data/lib/ollama/client/doc.rb +26 -0
  10. data/lib/ollama/client.rb +137 -0
  11. data/lib/ollama/commands/chat.rb +21 -0
  12. data/lib/ollama/commands/copy.rb +19 -0
  13. data/lib/ollama/commands/create.rb +20 -0
  14. data/lib/ollama/commands/delete.rb +19 -0
  15. data/lib/ollama/commands/embed.rb +21 -0
  16. data/lib/ollama/commands/embeddings.rb +20 -0
  17. data/lib/ollama/commands/generate.rb +21 -0
  18. data/lib/ollama/commands/ps.rb +19 -0
  19. data/lib/ollama/commands/pull.rb +19 -0
  20. data/lib/ollama/commands/push.rb +19 -0
  21. data/lib/ollama/commands/show.rb +20 -0
  22. data/lib/ollama/commands/tags.rb +19 -0
  23. data/lib/ollama/dto.rb +42 -0
  24. data/lib/ollama/errors.rb +15 -0
  25. data/lib/ollama/handlers/collector.rb +17 -0
  26. data/lib/ollama/handlers/concern.rb +31 -0
  27. data/lib/ollama/handlers/dump_json.rb +8 -0
  28. data/lib/ollama/handlers/dump_yaml.rb +8 -0
  29. data/lib/ollama/handlers/markdown.rb +22 -0
  30. data/lib/ollama/handlers/nop.rb +7 -0
  31. data/lib/ollama/handlers/print.rb +16 -0
  32. data/lib/ollama/handlers/progress.rb +36 -0
  33. data/lib/ollama/handlers/say.rb +19 -0
  34. data/lib/ollama/handlers/single.rb +17 -0
  35. data/lib/ollama/handlers.rb +13 -0
  36. data/lib/ollama/image.rb +31 -0
  37. data/lib/ollama/message.rb +9 -0
  38. data/lib/ollama/options.rb +68 -0
  39. data/lib/ollama/response.rb +5 -0
  40. data/lib/ollama/tool/function/parameters/property.rb +9 -0
  41. data/lib/ollama/tool/function/parameters.rb +10 -0
  42. data/lib/ollama/tool/function.rb +11 -0
  43. data/lib/ollama/tool.rb +9 -0
  44. data/lib/ollama/utils/ansi_markdown.rb +217 -0
  45. data/lib/ollama/utils/width.rb +22 -0
  46. data/lib/ollama/version.rb +8 -0
  47. data/lib/ollama.rb +43 -0
  48. data/ollama-ruby.gemspec +36 -0
  49. data/spec/assets/kitten.jpg +0 -0
  50. data/spec/ollama/client/doc_spec.rb +11 -0
  51. data/spec/ollama/client_spec.rb +144 -0
  52. data/spec/ollama/commands/chat_spec.rb +52 -0
  53. data/spec/ollama/commands/copy_spec.rb +28 -0
  54. data/spec/ollama/commands/create_spec.rb +37 -0
  55. data/spec/ollama/commands/delete_spec.rb +28 -0
  56. data/spec/ollama/commands/embed_spec.rb +52 -0
  57. data/spec/ollama/commands/embeddings_spec.rb +38 -0
  58. data/spec/ollama/commands/generate_spec.rb +29 -0
  59. data/spec/ollama/commands/ps_spec.rb +25 -0
  60. data/spec/ollama/commands/pull_spec.rb +28 -0
  61. data/spec/ollama/commands/push_spec.rb +28 -0
  62. data/spec/ollama/commands/show_spec.rb +28 -0
  63. data/spec/ollama/commands/tags_spec.rb +22 -0
  64. data/spec/ollama/handlers/collector_spec.rb +15 -0
  65. data/spec/ollama/handlers/dump_json_spec.rb +16 -0
  66. data/spec/ollama/handlers/dump_yaml_spec.rb +18 -0
  67. data/spec/ollama/handlers/markdown_spec.rb +46 -0
  68. data/spec/ollama/handlers/nop_spec.rb +15 -0
  69. data/spec/ollama/handlers/print_spec.rb +30 -0
  70. data/spec/ollama/handlers/progress_spec.rb +22 -0
  71. data/spec/ollama/handlers/say_spec.rb +30 -0
  72. data/spec/ollama/handlers/single_spec.rb +24 -0
  73. data/spec/ollama/image_spec.rb +23 -0
  74. data/spec/ollama/message_spec.rb +37 -0
  75. data/spec/ollama/options_spec.rb +25 -0
  76. data/spec/ollama/tool_spec.rb +78 -0
  77. data/spec/ollama/utils/ansi_markdown_spec.rb +15 -0
  78. data/spec/spec_helper.rb +16 -0
  79. metadata +321 -0
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Commands::Show do
4
+ it 'can be instantiated' do
5
+ show = described_class.new(name: 'llama3.1')
6
+ expect(show).to be_a described_class
7
+ end
8
+
9
+ it 'can be converted to JSON' do
10
+ show = described_class.new(name: 'llama3.1')
11
+ expect(show.as_json).to include(
12
+ name: 'llama3.1', stream: false
13
+ )
14
+ expect(show.to_json).to eq(
15
+ '{"json_class":"Ollama::Commands::Show","name":"llama3.1","stream":false}'
16
+ )
17
+ end
18
+
19
+ it 'can perform' do
20
+ show = described_class.new(name: 'llama3.1')
21
+ show.client = client = double('client')
22
+ expect(client).to receive(:request).with(
23
+ method: :post, path: '/api/show', handler: Ollama::Handlers::NOP ,stream: false,
24
+ body: '{"json_class":"Ollama::Commands::Show","name":"llama3.1","stream":false}'
25
+ )
26
+ show.perform(Ollama::Handlers::NOP)
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Commands::Tags do
4
+ let :tags do
5
+ described_class.new
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(tags).to be_a described_class
10
+ end
11
+
12
+ it 'cannot be converted to JSON' do
13
+ expect(tags).not_to respond_to(:as_json)
14
+ end
15
+
16
+ it 'can perform' do
17
+ tags.client = client = double('client')
18
+ expect(client).to receive(:request).
19
+ with(method: :get, path: '/api/tags', stream: false, handler: Ollama::Handlers::NOP)
20
+ tags.perform(Ollama::Handlers::NOP)
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Collector do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can collect responses in an array' do
10
+ collector = described_class.new(output:)
11
+ response = Ollama::Response[foo: 'testing']
12
+ collector.call(response)
13
+ expect(collector.result).to eq [ response ]
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::DumpJSON do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print pretty JSON' do
10
+ output = double('output')
11
+ expect(output).to receive(:puts).with(%Q'{\n "foo": "testing"\n}')
12
+ print = described_class.new(output:)
13
+ response = Ollama::Response[foo: 'testing']
14
+ print.call(response)
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::DumpYAML do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print YAML' do
10
+ output = double('output')
11
+ expect(output).to receive(:puts).with(
12
+ "--- !ruby/object:Ollama::Response\nfoo: testing\n"
13
+ )
14
+ print = described_class.new(output:)
15
+ response = Ollama::Response[foo: 'testing']
16
+ print.call(response)
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Markdown do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ let :md do
10
+ <<~end
11
+ - **strong**
12
+ - *emphasized*
13
+ end
14
+ end
15
+
16
+ let :ansi do
17
+ <<~end
18
+ · \e[1mstrong\e[0m
19
+
20
+ · \e[3memphasized\e[0m
21
+
22
+ end
23
+ end
24
+
25
+ it 'can markdown response as markdown' do
26
+ output = double('output', :sync= => true)
27
+ expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
28
+ expect(output).to receive(:puts)
29
+ markdown = described_class.new(output:)
30
+ response = double('response', response: md, done: false)
31
+ markdown.call(response)
32
+ response = double('response', response: nil, message: nil, done: true)
33
+ markdown.call(response)
34
+ end
35
+
36
+ it 'can markdown message content as markdown' do
37
+ output = double('output', :sync= => true)
38
+ expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
39
+ expect(output).to receive(:puts)
40
+ markdown = described_class.new(output:)
41
+ response = double('response', response: nil, message: double(content: md), done: false)
42
+ markdown.call(response)
43
+ response = double('response', response: nil, message: nil, done: true)
44
+ markdown.call(response)
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::NOP do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can do nothing at all' do
10
+ nop = described_class.new(output:)
11
+ response = Ollama::Response[foo: 'testing']
12
+ nop.call(response)
13
+ expect(nop.result).to be_nil
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Print do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print response' do
10
+ output = double('output', :sync= => true)
11
+ expect(output).to receive(:print).with('testing')
12
+ expect(output).to receive(:puts)
13
+ print = described_class.new(output:)
14
+ response = double('response', response: 'testing', done: false)
15
+ print.call(response)
16
+ response = double('response', response: nil, message: nil, done: true)
17
+ print.call(response)
18
+ end
19
+
20
+ it 'can print message content' do
21
+ output = double('output', :sync= => true)
22
+ expect(output).to receive(:print).with('testing')
23
+ expect(output).to receive(:puts)
24
+ print = described_class.new(output:)
25
+ response = double('response', response: nil, message: double(content: 'testing'), done: false)
26
+ print.call(response)
27
+ response = double('response', response: nil, message: nil, done: true)
28
+ print.call(response)
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Progress do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can display progress' do
10
+ response = double('response', status: 'testing', completed: 23, total: 666)
11
+ expect(infobar.counter).to receive(:progress).with(by: 23).and_call_original
12
+ expect(infobar.display).to receive(:update).and_call_original
13
+ described_class.new.call(response)
14
+ end
15
+
16
+ it 'can display errors in progress' do
17
+ response = double('response', error: 'foo', status: nil, completed: nil, total: nil)
18
+ progress = described_class.new
19
+ expect(progress.output).to receive(:puts).with(/Error: .*foo/)
20
+ progress.call(response)
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Say do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print response' do
10
+ output = double('output', :sync= => true)
11
+ expect(output).to receive(:print).with('testing')
12
+ expect(output).to receive(:close)
13
+ print = described_class.new(output:)
14
+ response = double('response', response: 'testing', done: false)
15
+ print.call(response)
16
+ response = double('response', response: nil, message: nil, done: true)
17
+ print.call(response)
18
+ end
19
+
20
+ it 'can print message content' do
21
+ output = double('output', :sync= => true)
22
+ expect(output).to receive(:print).with('testing')
23
+ expect(output).to receive(:close)
24
+ print = described_class.new(output:)
25
+ response = double('response', response: nil, message: double(content: 'testing'), done: false)
26
+ print.call(response)
27
+ response = double('response', response: nil, message: nil, done: true)
28
+ print.call(response)
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Single do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can collect responses in an array' do
10
+ single = described_class.new(output:)
11
+ response1 = Ollama::Response[foo: 'testing1']
12
+ response2 = Ollama::Response[foo: 'testing2']
13
+ single.call(response1)
14
+ single.call(response2)
15
+ expect(single.result).to eq [ response1, response2 ]
16
+ end
17
+
18
+ it 'can return only the single result' do
19
+ single = described_class.new(output:)
20
+ response = Ollama::Response[foo: 'testing']
21
+ single.call(response)
22
+ expect(single.result).to eq response
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Image do
4
+ let :image do
5
+ described_class.for_filename(asset('kitten.jpg'))
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(image).to be_a described_class
10
+ end
11
+
12
+ it 'cannot be created via .new' do
13
+ expect {
14
+ described_class.new('nix')
15
+ }.to raise_error NoMethodError
16
+ end
17
+
18
+ it 'can be converted to base64 string' do
19
+ expect(image.to_s.size).to eq 134400
20
+ expect(image.to_s.sum).to eq 42460
21
+ expect(image.to_s[0, 40]).to eq '/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAA'
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Message do
4
+ let :image do
5
+ Ollama::Image.for_string("test")
6
+ end
7
+
8
+ let :message do
9
+ described_class.new(
10
+ role: 'user',
11
+ content: 'hello world',
12
+ images: image
13
+ )
14
+ end
15
+
16
+ it 'can be instantiated' do
17
+ expect(message).to be_a described_class
18
+ end
19
+
20
+ it 'can be converted to JSON' do
21
+ expect(message.as_json).to eq(
22
+ json_class: described_class.name,
23
+ role: 'user',
24
+ content: 'hello world',
25
+ images: [ image ],
26
+ )
27
+ expect(message.to_json).to eq(
28
+ '{"json_class":"Ollama::Message","role":"user","content":"hello world","images":["dGVzdA==\n"]}'
29
+ )
30
+ end
31
+
32
+ it 'can be restored from JSON' do
33
+ expect(JSON(<<~'end', create_additions: true)).to be_a described_class
34
+ {"json_class":"Ollama::Message","role":"user","content":"hello world","images":["dGVzdA==\n"]}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Options do
4
+ let :options do
5
+ described_class.new(
6
+ penalize_newline: true,
7
+ num_ctx: 8192,
8
+ temperature: 0.7,
9
+ )
10
+ end
11
+
12
+ it 'can be instantiated' do
13
+ expect(options).to be_a described_class
14
+ end
15
+
16
+ it 'throws error for invalid types' do
17
+ expect { described_class.new(temperature: Class.new) }.
18
+ to raise_error(TypeError)
19
+ end
20
+
21
+ it 'throws error for invalid boolean values' do
22
+ expect { described_class.new(penalize_newline: :tertium) }.
23
+ to raise_error(TypeError)
24
+ end
25
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Tool do
4
+ let :location do
5
+ Ollama::Tool::Function::Parameters::Property.new(
6
+ type: 'string',
7
+ description: 'The location to get the weather for, e.g. Berlin, Berlin',
8
+ )
9
+ end
10
+
11
+ let :format do
12
+ Ollama::Tool::Function::Parameters::Property.new(
13
+ type: 'string',
14
+ description: "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
15
+ enum: %w[ celsius fahrenheit ]
16
+ )
17
+ end
18
+
19
+ let :parameters do
20
+ Ollama::Tool::Function::Parameters.new(
21
+ type: 'object',
22
+ properties: { location:, format: },
23
+ required: %w[ location format ],
24
+ )
25
+ end
26
+
27
+ let :function do
28
+ Ollama::Tool::Function.new(
29
+ name: 'get_current_weather',
30
+ description: 'Get the current weather for a location',
31
+ parameters:,
32
+ )
33
+ end
34
+
35
+ let :tool do
36
+ described_class.new(
37
+ type: 'function',
38
+ function:,
39
+ )
40
+ end
41
+
42
+ it 'can be instantiated' do
43
+ expect(tool).to be_a described_class
44
+ end
45
+
46
+ it 'cannot be converted to JSON' do
47
+ expect(tool.as_json).to eq(
48
+ json_class: described_class.name,
49
+ type: 'function',
50
+ function: {
51
+ json_class: "Ollama::Tool::Function",
52
+ name: 'get_current_weather',
53
+ description: "Get the current weather for a location",
54
+ parameters: {
55
+ json_class: "Ollama::Tool::Function::Parameters",
56
+ type: "object",
57
+ properties: {
58
+ location: {
59
+ json_class: "Ollama::Tool::Function::Parameters::Property",
60
+ type: "string",
61
+ description: "The location to get the weather for, e.g. Berlin, Berlin"
62
+ },
63
+ format: {
64
+ json_class: "Ollama::Tool::Function::Parameters::Property",
65
+ type: "string",
66
+ description: "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
67
+ enum: ["celsius", "fahrenheit"]
68
+ }
69
+ },
70
+ required: ["location", "format"]
71
+ },
72
+ }
73
+ )
74
+ expect(tool.to_json).to eq(
75
+ %{{"json_class":"Ollama::Tool","type":"function","function":{"json_class":"Ollama::Tool::Function","name":"get_current_weather","description":"Get the current weather for a location","parameters":{"json_class":"Ollama::Tool::Function::Parameters","type":"object","properties":{"location":{"json_class":"Ollama::Tool::Function::Parameters::Property","type":"string","description":"The location to get the weather for, e.g. Berlin, Berlin"},"format":{"json_class":"Ollama::Tool::Function::Parameters::Property","type":"string","description":"The format to return the weather in, e.g. 'celsius' or 'fahrenheit'","enum":["celsius","fahrenheit"]}},"required":["location","format"]}}}}
76
+ )
77
+ end
78
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Utils::ANSIMarkdown do
4
+ let :source do
5
+ File.read(Pathname.new(__dir__) + '..' + '..' + '..' + 'README.md')
6
+ end
7
+
8
+ it 'can parse' do
9
+ File.open('tmp/README.ansi', ?w) do |output|
10
+ ansi = described_class.parse(source)
11
+ expect(ansi).to match("This is the end.")
12
+ output.puts ansi
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
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
+ begin
9
+ require 'debug'
10
+ rescue LoadError
11
+ end
12
+ require 'ollama'
13
+
14
+ def asset(name)
15
+ File.join(__dir__, 'assets', name)
16
+ end