elelem-ollama 0.1.0 → 0.2.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/.rspec +1 -0
- data/Rakefile +5 -1
- data/lib/elelem/ollama/client.rb +34 -17
- data/lib/elelem/ollama/version.rb +1 -1
- data/lib/elelem/ollama.rb +9 -5
- data/spec/elelem/ollama/client_spec.rb +86 -0
- data/spec/spec_helper.rb +11 -0
- metadata +8 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 103d5e51990858afaab14fe8042a85b0a05517e2896535677b29adbd60e861d7
|
|
4
|
+
data.tar.gz: c45b6f097c19dfb9515c80a4ff742a190b8d950ca414ae6d3be071171f6ffd99
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc6342db3a78a8cc206f2547b88b9af13eeb0ee146ba8d7dd1698695839559cdba5292e45894ceb916fc6978cc19735535078713bcd2032b0d9b2f16e602a31e
|
|
7
|
+
data.tar.gz: 61560a4fd4857899427828c20b9121ff7f0237b98f652d1b32b3612056f7b8451282251051a0d75794bb3b268d22bb9a3f92c8287f5147fba4b829e3611ff064
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/Rakefile
CHANGED
data/lib/elelem/ollama/client.rb
CHANGED
|
@@ -6,12 +6,13 @@ module Elelem
|
|
|
6
6
|
def initialize(
|
|
7
7
|
model:,
|
|
8
8
|
host: "localhost:11434",
|
|
9
|
-
think:
|
|
10
|
-
keep_alive:
|
|
9
|
+
think: nil,
|
|
10
|
+
keep_alive: nil,
|
|
11
11
|
options: {},
|
|
12
12
|
params: {},
|
|
13
13
|
read_timeout: 3600,
|
|
14
|
-
open_timeout: 10
|
|
14
|
+
open_timeout: 10,
|
|
15
|
+
http: nil
|
|
15
16
|
)
|
|
16
17
|
@uri = normalize_uri(host)
|
|
17
18
|
@model = model
|
|
@@ -21,6 +22,7 @@ module Elelem
|
|
|
21
22
|
@params = params
|
|
22
23
|
@read_timeout = read_timeout
|
|
23
24
|
@open_timeout = open_timeout
|
|
25
|
+
@http = http
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def fetch(messages, tools = [], &block)
|
|
@@ -103,24 +105,17 @@ module Elelem
|
|
|
103
105
|
end
|
|
104
106
|
|
|
105
107
|
def stream(body)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
request.body = JSON.generate(body)
|
|
108
|
+
http.post(@uri, body: body) do |response|
|
|
109
|
+
raise "HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
http.use_ssl = @uri.scheme == "https"
|
|
112
|
-
http.read_timeout = @read_timeout
|
|
113
|
-
http.open_timeout = @open_timeout
|
|
114
|
-
|
|
115
|
-
http.start do |conn|
|
|
116
|
-
conn.request(request) do |response|
|
|
117
|
-
raise "HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
|
118
|
-
|
|
119
|
-
read_ndjson_stream(response) { |event| yield event }
|
|
120
|
-
end
|
|
111
|
+
read_ndjson_stream(response) { |event| yield event }
|
|
121
112
|
end
|
|
122
113
|
end
|
|
123
114
|
|
|
115
|
+
def http
|
|
116
|
+
@http ||= HttpAdapter.new(read_timeout: @read_timeout, open_timeout: @open_timeout)
|
|
117
|
+
end
|
|
118
|
+
|
|
124
119
|
def read_ndjson_stream(response)
|
|
125
120
|
buffer = String.new
|
|
126
121
|
|
|
@@ -145,6 +140,28 @@ module Elelem
|
|
|
145
140
|
}
|
|
146
141
|
end
|
|
147
142
|
end
|
|
143
|
+
|
|
144
|
+
class HttpAdapter
|
|
145
|
+
def initialize(read_timeout:, open_timeout:)
|
|
146
|
+
@read_timeout = read_timeout
|
|
147
|
+
@open_timeout = open_timeout
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def post(uri, body:)
|
|
151
|
+
request = Net::HTTP::Post.new(uri)
|
|
152
|
+
request["content-type"] = "application/json"
|
|
153
|
+
request.body = JSON.generate(body)
|
|
154
|
+
|
|
155
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
156
|
+
http.use_ssl = uri.scheme == "https"
|
|
157
|
+
http.read_timeout = @read_timeout
|
|
158
|
+
http.open_timeout = @open_timeout
|
|
159
|
+
|
|
160
|
+
http.start do |conn|
|
|
161
|
+
conn.request(request) { |response| yield response }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
148
165
|
end
|
|
149
166
|
end
|
|
150
167
|
end
|
data/lib/elelem/ollama.rb
CHANGED
|
@@ -8,9 +8,13 @@ require "uri"
|
|
|
8
8
|
require_relative "ollama/version"
|
|
9
9
|
require_relative "ollama/client"
|
|
10
10
|
|
|
11
|
-
Elelem
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
Elelem.configure do |config|
|
|
12
|
+
config.provider(:ollama) do
|
|
13
|
+
Elelem::Ollama::Client.new(
|
|
14
|
+
model: ENV.fetch("OLLAMA_MODEL", "gpt-oss:latest"),
|
|
15
|
+
host: ENV.fetch("OLLAMA_HOST", "localhost:11434"),
|
|
16
|
+
think: ENV["OLLAMA_THINK"],
|
|
17
|
+
keep_alive: ENV["OLLAMA_KEEP_ALIVE"]
|
|
18
|
+
)
|
|
19
|
+
end
|
|
16
20
|
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Elelem::Ollama::Client do
|
|
4
|
+
subject(:client) { described_class.new(model: "gpt-oss:latest", http:, **params) }
|
|
5
|
+
|
|
6
|
+
let(:params) { {} }
|
|
7
|
+
let(:messages) { [{ role: "user", content: "hi" }] }
|
|
8
|
+
let(:body) { http.body }
|
|
9
|
+
|
|
10
|
+
let(:response) do
|
|
11
|
+
::Net::HTTPOK.new("1.1", "200", "OK").tap do |it|
|
|
12
|
+
allow(it).to receive(:read_body).and_yield(%({"done":true,"message":{}}\n))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:http) do
|
|
17
|
+
Class.new do
|
|
18
|
+
attr_reader :body
|
|
19
|
+
|
|
20
|
+
def initialize(response)
|
|
21
|
+
@response = response
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def post(_url, body:)
|
|
25
|
+
@body = body
|
|
26
|
+
yield @response
|
|
27
|
+
end
|
|
28
|
+
end.new(response)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "#fetch" do
|
|
32
|
+
it "sends only model, messages and stream by default" do
|
|
33
|
+
client.fetch(messages) { }
|
|
34
|
+
|
|
35
|
+
expect(body).to eq(model: "gpt-oss:latest", messages:, stream: true)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "sends tools when present" do
|
|
39
|
+
tools = [{ type: "function", function: { name: "read" } }]
|
|
40
|
+
|
|
41
|
+
client.fetch(messages, tools) { }
|
|
42
|
+
|
|
43
|
+
expect(body[:tools]).to eq(tools)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "with tuning keywords" do
|
|
47
|
+
let(:params) { { think: "high", keep_alive: "30m", options: { num_ctx: 32_768 } } }
|
|
48
|
+
|
|
49
|
+
it "sends them in the request body" do
|
|
50
|
+
client.fetch(messages) { }
|
|
51
|
+
|
|
52
|
+
expect(body).to include(think: "high", keep_alive: "30m", options: { num_ctx: 32_768 })
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "with an empty options hash" do
|
|
57
|
+
let(:params) { { options: {} } }
|
|
58
|
+
|
|
59
|
+
it "omits options" do
|
|
60
|
+
client.fetch(messages) { }
|
|
61
|
+
|
|
62
|
+
expect(body).not_to have_key(:options)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "with passthrough params" do
|
|
67
|
+
let(:params) { { params: { format: "json", truncate: false, top_logprobs: 3 } } }
|
|
68
|
+
|
|
69
|
+
it "merges them into the request body" do
|
|
70
|
+
client.fetch(messages) { }
|
|
71
|
+
|
|
72
|
+
expect(body).to include(format: "json", truncate: false, top_logprobs: 3)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context "with a passthrough param that collides with a keyword" do
|
|
77
|
+
let(:params) { { think: "low", params: { think: "high" } } }
|
|
78
|
+
|
|
79
|
+
it "prefers the passthrough value" do
|
|
80
|
+
client.fetch(messages) { }
|
|
81
|
+
|
|
82
|
+
expect(body[:think]).to eq("high")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elelem-ollama
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mo khan
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.
|
|
18
|
+
version: '0.11'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0.
|
|
25
|
+
version: '0.11'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: json
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '3.0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '3.0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: net-http
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -73,12 +73,15 @@ executables:
|
|
|
73
73
|
extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
|
75
75
|
files:
|
|
76
|
+
- ".rspec"
|
|
76
77
|
- LICENSE.txt
|
|
77
78
|
- Rakefile
|
|
78
79
|
- exe/elelem-ollama
|
|
79
80
|
- lib/elelem/ollama.rb
|
|
80
81
|
- lib/elelem/ollama/client.rb
|
|
81
82
|
- lib/elelem/ollama/version.rb
|
|
83
|
+
- spec/elelem/ollama/client_spec.rb
|
|
84
|
+
- spec/spec_helper.rb
|
|
82
85
|
homepage: https://src.mokhan.ca/elelem/ollama
|
|
83
86
|
licenses:
|
|
84
87
|
- MIT
|