huginn_ollama_agent 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e3fa742e907e01a2d71241761e082ab51bc8488192278df8693523927eef9a7e
4
+ data.tar.gz: 618ef39a32e3ccc4ca3da53cc2d8f1876cc3706ad53d2cb66e1e928a19f3bc9f
5
+ SHA512:
6
+ metadata.gz: 5398a357bdd6c5a22ef0ce4778f3d77a1f4bed97a50de6d4da54cd86e1a4093d1df3dd141b4656b57887d44c97bd45d51056aa665d805c8a84c0de8b6b4b71f4
7
+ data.tar.gz: aa2abc6c4a51a51d2274507159bb1c75dd9d37a1ca88946ef1a142234b769b17806853146b97cf09de2c9fc59292db91e2d0ab323a88d5c7cc8a90597ed4f924
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2024 Nicolas Germain
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,299 @@
1
+ module Agents
2
+ class OllamaAgent < Agent
3
+ include FormConfigurable
4
+ can_dry_run!
5
+ no_bulk_receive!
6
+ default_schedule 'every_1h'
7
+
8
+ description do
9
+ <<-MD
10
+ The Ollama Agent interacts with Ollama's api.
11
+
12
+ The `type` can be like generate.
13
+
14
+ `debug` is used for verbose mode.
15
+
16
+ `model` (required) the model name.
17
+
18
+ `prompt` the prompt to generate a response.
19
+
20
+ `context` the context parameter returned from a previous request to /generate, this can be used to keep a short conversational memory.
21
+
22
+ `stream` if false the response will be returned as a single response object, rather than a stream of objects.
23
+
24
+ `raw` if true no formatting will be applied to the prompt. You may choose to use the raw parameter if you are specifying a full templated prompt in your request to the API.
25
+
26
+ `expected_receive_period_in_days` is used to determine if the Agent is working. Set it to the maximum number of days
27
+ that you anticipate passing without this Agent receiving an incoming Event.
28
+ MD
29
+ end
30
+
31
+ event_description <<-MD
32
+ Events look like this:
33
+
34
+ {
35
+ "model": "mistral",
36
+ "created_at": "2024-03-25T20:54:25.920292408Z",
37
+ "response": " I'm assuming you're asking if the question or instruction provided in the prompt is clear and effective. To answer that, I would need to see the specific prompt you have in mind. If you could please share it with me, I'd be happy to help evaluate its clarity and effectiveness.",
38
+ "done": true,
39
+ "context": [
40
+ 733,
41
+ 16289,
42
+ 28793,
43
+ 28705,
44
+ 1235,
45
+ 272,
46
+ 11510,
47
+ 771,
48
+ 1550,
49
+ 733,
50
+ 28748,
51
+ 16289,
52
+ 28793,
53
+ 315,
54
+ 28742,
55
+ 28719,
56
+ 16347,
57
+ 368,
58
+ 28742,
59
+ 267,
60
+ 7201,
61
+ 513,
62
+ 272,
63
+ 2996,
64
+ 442,
65
+ 13126,
66
+ 3857,
67
+ 297,
68
+ 272,
69
+ 11510,
70
+ 349,
71
+ 3081,
72
+ 304,
73
+ 5645,
74
+ 28723,
75
+ 1791,
76
+ 4372,
77
+ 369,
78
+ 28725,
79
+ 315,
80
+ 682,
81
+ 927,
82
+ 298,
83
+ 1032,
84
+ 272,
85
+ 2948,
86
+ 11510,
87
+ 368,
88
+ 506,
89
+ 297,
90
+ 2273,
91
+ 28723,
92
+ 1047,
93
+ 368,
94
+ 829,
95
+ 4665,
96
+ 4098,
97
+ 378,
98
+ 395,
99
+ 528,
100
+ 28725,
101
+ 315,
102
+ 28742,
103
+ 28715,
104
+ 347,
105
+ 4610,
106
+ 298,
107
+ 1316,
108
+ 15627,
109
+ 871,
110
+ 25312,
111
+ 304,
112
+ 23798,
113
+ 28723
114
+ ],
115
+ "total_duration": 12033891123,
116
+ "load_duration": 921427821,
117
+ "prompt_eval_count": 14,
118
+ "prompt_eval_duration": 1246297000,
119
+ "eval_count": 62,
120
+ "eval_duration": 9865732000
121
+ }
122
+
123
+ MD
124
+
125
+ def default_options
126
+ {
127
+ 'type' => 'generate',
128
+ 'model' => 'mistral',
129
+ 'url' => '',
130
+ 'prompt' => '',
131
+ 'context' => '',
132
+ 'stream' => 'false',
133
+ 'raw' => 'false',
134
+ 'debug' => 'false',
135
+ 'expected_receive_period_in_days' => '2',
136
+ }
137
+ end
138
+
139
+ form_configurable :type, type: :array, values: ['generate']
140
+ form_configurable :model, type: :string
141
+ form_configurable :url, type: :string
142
+ form_configurable :prompt, type: :string
143
+ form_configurable :context, type: :string
144
+ form_configurable :stream, type: :boolean
145
+ form_configurable :raw, type: :boolean
146
+ form_configurable :debug, type: :boolean
147
+ form_configurable :expected_receive_period_in_days, type: :string
148
+ def validate_options
149
+ errors.add(:base, "type has invalid value: should be 'generate'") if interpolated['type'].present? && !%w(generate).include?(interpolated['type'])
150
+
151
+ unless options['model'].present?
152
+ errors.add(:base, "model is a required field")
153
+ end
154
+
155
+ unless options['url'].present?
156
+ errors.add(:base, "url is a required field")
157
+ end
158
+
159
+ unless options['prompt'].present?
160
+ errors.add(:base, "prompt is a required field")
161
+ end
162
+
163
+ if options.has_key?('stream') && boolify(options['stream']).nil?
164
+ errors.add(:base, "if provided, stream must be true or false")
165
+ end
166
+
167
+ if options.has_key?('raw') && boolify(options['raw']).nil?
168
+ errors.add(:base, "if provided, raw must be true or false")
169
+ end
170
+
171
+ if options.has_key?('debug') && boolify(options['debug']).nil?
172
+ errors.add(:base, "if provided, debug must be true or false")
173
+ end
174
+
175
+ unless options['expected_receive_period_in_days'].present? && options['expected_receive_period_in_days'].to_i > 0
176
+ errors.add(:base, "Please provide 'expected_receive_period_in_days' to indicate how many days can pass before this Agent is considered to be not working")
177
+ end
178
+ end
179
+
180
+ def working?
181
+ event_created_within?(options['expected_receive_period_in_days']) && !recent_error_logs?
182
+ end
183
+
184
+ def receive(incoming_events)
185
+ incoming_events.each do |event|
186
+ interpolate_with(event) do
187
+ log event
188
+ trigger_action
189
+ end
190
+ end
191
+ end
192
+
193
+ def check
194
+ trigger_action
195
+ end
196
+
197
+ private
198
+
199
+
200
+ def log_curl_output(code,body)
201
+
202
+ log "request status : #{code}"
203
+
204
+ if interpolated['debug'] == 'true'
205
+ log "request status : #{code}"
206
+ log "body"
207
+ log body
208
+ end
209
+
210
+ end
211
+
212
+ def push_model(model)
213
+
214
+ uri = URI.parse(interpolated['url'] + '/api/pull')
215
+ request = Net::HTTP::Post.new(uri)
216
+ request.body = JSON.dump({
217
+ "name" => interpolated['model']
218
+ })
219
+
220
+ req_options = {
221
+ use_ssl: uri.scheme == "https",
222
+ }
223
+
224
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
225
+ http.request(request)
226
+ end
227
+
228
+ log_curl_output(response.code,response.body)
229
+
230
+ end
231
+
232
+ def check_remote_model()
233
+ url = URI(interpolated['url'] + '/api/tags')
234
+ https = Net::HTTP.new(url.host, url.port)
235
+
236
+ request = Net::HTTP::Get.new(url)
237
+ response = https.request(request)
238
+
239
+ log_curl_output(response.code,response.body)
240
+
241
+ payload = JSON.parse(response.body)
242
+ payload['models'].each do |modele|
243
+ return true if modele['name'] == interpolated['model'] + ':latest'
244
+ end
245
+
246
+ false
247
+ end
248
+
249
+ def generate_completion()
250
+
251
+ request_payload = {}
252
+ request_payload['model'] = interpolated['model']
253
+ request_payload['prompt'] = interpolated['prompt']
254
+ request_payload['stream'] = boolify(interpolated['stream'])
255
+ request_payload['raw'] = boolify(interpolated['raw'])
256
+ request_payload['context'] = context if !interpolated['context'].empty?
257
+
258
+ if check_remote_model()
259
+ if interpolated['debug'] == 'true'
260
+ log "#{interpolated['model']} found"
261
+ end
262
+ else
263
+ if interpolated['debug'] == 'true'
264
+ log "#{interpolated['model']} not found"
265
+ end
266
+ push_model(interpolated['model'])
267
+ end
268
+
269
+ uri = URI.parse(interpolated['url'] + '/api/generate')
270
+ request = Net::HTTP::Post.new(uri)
271
+ request.body = JSON.dump(request_payload)
272
+
273
+ req_options = {
274
+ use_ssl: uri.scheme == "https",
275
+ }
276
+
277
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
278
+ http.request(request)
279
+ end
280
+
281
+ log_curl_output(response.code,response.body)
282
+
283
+ payload = JSON.parse(response.body)
284
+ create_event payload: payload
285
+
286
+ end
287
+
288
+
289
+ def trigger_action
290
+
291
+ case interpolated['type']
292
+ when "generate"
293
+ generate_completion()
294
+ else
295
+ log "Error: type has an invalid value (#{type})"
296
+ end
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,4 @@
1
+ require 'huginn_agent'
2
+
3
+ #HuginnAgent.load 'huginn_ollama_agent/concerns/my_agent_concern'
4
+ HuginnAgent.register 'huginn_ollama_agent/ollama_agent'
@@ -0,0 +1,13 @@
1
+ require 'rails_helper'
2
+ require 'huginn_agent/spec_helper'
3
+
4
+ describe Agents::OllamaAgent do
5
+ before(:each) do
6
+ @valid_options = Agents::OllamaAgent.new.default_options
7
+ @checker = Agents::OllamaAgent.new(:name => "OllamaAgent", :options => @valid_options)
8
+ @checker.user = users(:bob)
9
+ @checker.save!
10
+ end
11
+
12
+ pending "add specs here"
13
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: huginn_ollama_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas Germain
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-03-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 12.3.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 12.3.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: huginn_agent
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Write a longer description or delete this line.
56
+ email:
57
+ - ngermain@hihouhou.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE.txt
63
+ - lib/huginn_ollama_agent.rb
64
+ - lib/huginn_ollama_agent/ollama_agent.rb
65
+ - spec/ollama_agent_spec.rb
66
+ homepage: https://github.com/hihouhou/huginn_ollama_agent
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.3.3
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Write a short summary, because Rubygems requires one.
89
+ test_files:
90
+ - spec/ollama_agent_spec.rb