ollama-ai 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ollama
4
+ module Errors
5
+ class OllamaError < StandardError
6
+ def initialize(message = nil)
7
+ super(message)
8
+ end
9
+ end
10
+
11
+ class BlockWithoutServerSentEventsError < OllamaError; end
12
+
13
+ class RequestError < OllamaError
14
+ attr_reader :request, :payload
15
+
16
+ def initialize(message = nil, request: nil, payload: nil)
17
+ @request = request
18
+ @payload = payload
19
+
20
+ super(message)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+
6
+ require_relative '../ports/dsl/ollama-ai/errors'
7
+
8
+ module Ollama
9
+ module Controllers
10
+ class Client
11
+ DEFAULT_ADDRESS = 'http://localhost:11434'
12
+
13
+ ALLOWED_REQUEST_OPTIONS = %i[timeout open_timeout read_timeout write_timeout].freeze
14
+
15
+ def initialize(config)
16
+ @server_sent_events = config.dig(:options, :server_sent_events)
17
+
18
+ @address = if config[:credentials][:address].nil? || config[:credentials][:address].to_s.strip.empty?
19
+ "#{DEFAULT_ADDRESS}/"
20
+ else
21
+ "#{config[:credentials][:address].to_s.sub(%r{/$}, '')}/"
22
+ end
23
+
24
+ @request_options = config.dig(:options, :connection, :request)
25
+
26
+ @request_options = if @request_options.is_a?(Hash)
27
+ @request_options.select do |key, _|
28
+ ALLOWED_REQUEST_OPTIONS.include?(key)
29
+ end
30
+ else
31
+ {}
32
+ end
33
+ end
34
+
35
+ def generate(payload, server_sent_events: nil, &callback)
36
+ request('api/generate', payload, server_sent_events:, &callback)
37
+ end
38
+
39
+ def chat(payload, server_sent_events: nil, &callback)
40
+ request('api/chat', payload, server_sent_events:, &callback)
41
+ end
42
+
43
+ def create(payload, server_sent_events: nil, &callback)
44
+ request('api/create', payload, server_sent_events:, &callback)
45
+ end
46
+
47
+ def tags(server_sent_events: nil, &callback)
48
+ request('api/tags', nil, server_sent_events:, request_method: 'GET', &callback)
49
+ end
50
+
51
+ def show(payload, server_sent_events: nil, &callback)
52
+ request('api/show', payload, server_sent_events:, &callback)
53
+ end
54
+
55
+ def copy(payload, server_sent_events: nil, &callback)
56
+ request('api/copy', payload, server_sent_events:, &callback)
57
+ true
58
+ end
59
+
60
+ def delete(payload, server_sent_events: nil, &callback)
61
+ request('api/delete', payload, server_sent_events:, request_method: 'DELETE', &callback)
62
+ true
63
+ end
64
+
65
+ def pull(payload, server_sent_events: nil, &callback)
66
+ request('api/pull', payload, server_sent_events:, &callback)
67
+ end
68
+
69
+ def push(payload, server_sent_events: nil, &callback)
70
+ request('api/push', payload, server_sent_events:, &callback)
71
+ end
72
+
73
+ def embeddings(payload, server_sent_events: nil, &callback)
74
+ request('api/embeddings', payload, server_sent_events:, &callback)
75
+ end
76
+
77
+ def request(path, payload = nil, server_sent_events: nil, request_method: 'POST', &callback)
78
+ server_sent_events_enabled = server_sent_events.nil? ? @server_sent_events : server_sent_events
79
+ url = "#{@address}#{path}"
80
+
81
+ if !callback.nil? && !server_sent_events_enabled
82
+ raise BlockWithoutServerSentEventsError,
83
+ 'You are trying to use a block without Server Sent Events (SSE) enabled.'
84
+ end
85
+
86
+ results = []
87
+
88
+ method_to_call = request_method.to_s.strip.downcase.to_sym
89
+
90
+ partial_json = ''
91
+
92
+ response = Faraday.new(request: @request_options) do |faraday|
93
+ faraday.response :raise_error
94
+ end.send(method_to_call) do |request|
95
+ request.url url
96
+ request.headers['Content-Type'] = 'application/json'
97
+
98
+ request.body = payload.to_json unless payload.nil?
99
+
100
+ if server_sent_events_enabled
101
+ request.options.on_data = proc do |chunk, bytes, env|
102
+ if env && env.status != 200
103
+ raise_error = Faraday::Response::RaiseError.new
104
+ raise_error.on_complete(env.merge(body: chunk))
105
+ end
106
+
107
+ partial_json += chunk
108
+
109
+ parsed_json = safe_parse_json(partial_json)
110
+
111
+ if parsed_json
112
+ result = { event: parsed_json, raw: { chunk:, bytes:, env: } }
113
+
114
+ callback.call(result[:event], result[:raw]) unless callback.nil?
115
+
116
+ results << result
117
+
118
+ partial_json = ''
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ return safe_parse_jsonl(response.body) unless server_sent_events_enabled
125
+
126
+ results.map { |result| result[:event] }
127
+ rescue Faraday::Error => e
128
+ raise RequestError.new(e.message, request: e, payload:)
129
+ end
130
+
131
+ def safe_parse_json(raw)
132
+ raw.to_s.lstrip.start_with?('{', '[') ? JSON.parse(raw) : nil
133
+ rescue JSON::ParserError
134
+ nil
135
+ end
136
+
137
+ def safe_parse_jsonl(raw)
138
+ raw.to_s.lstrip.start_with?('{', '[') ? raw.split("\n").map { |line| JSON.parse(line) } : raw
139
+ rescue JSON::ParserError
140
+ raw
141
+ end
142
+ end
143
+ end
144
+ end
data/ollama-ai.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'static/gem'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = Ollama::GEM[:name]
7
+ spec.version = Ollama::GEM[:version]
8
+ spec.authors = [Ollama::GEM[:author]]
9
+
10
+ spec.summary = Ollama::GEM[:summary]
11
+ spec.description = Ollama::GEM[:description]
12
+
13
+ spec.homepage = Ollama::GEM[:github]
14
+
15
+ spec.license = Ollama::GEM[:license]
16
+
17
+ spec.required_ruby_version = Gem::Requirement.new(">= #{Ollama::GEM[:ruby]}")
18
+
19
+ spec.metadata['allowed_push_host'] = Ollama::GEM[:gem_server]
20
+
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = Ollama::GEM[:github]
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{\A(?:test|spec|features)/})
27
+ end
28
+ end
29
+
30
+ spec.require_paths = ['ports/dsl']
31
+
32
+ spec.add_dependency 'faraday', '~> 2.8'
33
+
34
+ spec.metadata['rubygems_mfa_required'] = 'true'
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../components/errors'
4
+
5
+ include Ollama::Errors
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../static/gem'
4
+ require_relative '../../controllers/client'
5
+
6
+ module Ollama
7
+ def self.new(...)
8
+ Controllers::Client.new(...)
9
+ end
10
+
11
+ def self.version
12
+ Ollama::GEM[:version]
13
+ end
14
+ end
data/static/gem.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ollama
4
+ GEM = {
5
+ name: 'ollama-ai',
6
+ version: '1.0.0',
7
+ author: 'gbaptista',
8
+ summary: 'Interact with Ollama API to run open source AI models locally.',
9
+ description: "A Ruby gem for interacting with Ollama's API that allows you to run open source AI LLMs (Large Language Models) locally.",
10
+ github: 'https://github.com/gbaptista/ollama-ai',
11
+ gem_server: 'https://rubygems.org',
12
+ license: 'MIT',
13
+ ruby: '3.1.0'
14
+ }.freeze
15
+ end
@@ -0,0 +1,39 @@
1
+ (require '[clojure.string :as str])
2
+
3
+ (defn slugify [text]
4
+ (-> text
5
+ (clojure.string/lower-case)
6
+ (clojure.string/replace " " "-")
7
+ (clojure.string/replace #"[^a-z0-9\-_]" "")))
8
+
9
+ (defn remove-code-blocks [content]
10
+ (let [code-block-regex #"(?s)```.*?```"]
11
+ (clojure.string/replace content code-block-regex "")))
12
+
13
+ (defn process-line [line]
14
+ (when-let [[_ hashes title] (re-find #"^(\#{2,}) (.+)" line)]
15
+ (let [link (slugify title)]
16
+ {:level (count hashes) :title title :link link})))
17
+
18
+ (defn create-index [content]
19
+ (let [processed-content (remove-code-blocks content)
20
+ processed-lines (->> processed-content
21
+ clojure.string/split-lines
22
+ (map process-line)
23
+ (remove nil?))]
24
+ (->> processed-lines
25
+ (map (fn [{:keys [level title link]}]
26
+ (str (apply str (repeat (* 4 (- level 2)) " "))
27
+ "- ["
28
+ title
29
+ "](#"
30
+ link
31
+ ")")))
32
+ (clojure.string/join "\n"))))
33
+
34
+
35
+ (let [content (slurp "template.md")
36
+ index (create-index content)
37
+ updated-content (clojure.string/replace content "{index}" index)]
38
+ (spit "README.md" updated-content)
39
+ (println "README.md successfully generated."))