gemini-ai 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'event_stream_parser'
4
+ require 'faraday'
5
+ require 'json'
6
+ require 'googleauth'
7
+
8
+ module Gemini
9
+ module Controllers
10
+ class Client
11
+ def initialize(config)
12
+ @authorizer = ::Google::Auth::ServiceAccountCredentials.make_creds(
13
+ json_key_io: File.open(config[:credentials][:file_path]),
14
+ scope: 'https://www.googleapis.com/auth/cloud-platform'
15
+ )
16
+
17
+ @address = "https://#{config[:credentials][:region]}-aiplatform.googleapis.com/v1/projects/#{config[:credentials][:project_id]}/locations/#{config[:credentials][:region]}/publishers/google/models/#{config[:settings][:model]}"
18
+
19
+ @stream = config[:settings][:stream]
20
+ end
21
+
22
+ def stream_generate_content(payload, stream: nil, &callback)
23
+ request('streamGenerateContent', payload, stream:, &callback)
24
+ end
25
+
26
+ def request(path, payload, stream: nil, &callback)
27
+ stream_enabled = stream.nil? ? @stream : stream
28
+ url = "#{@address}:#{path}"
29
+ url += '?alt=sse' if stream_enabled
30
+
31
+ if !callback.nil? && !stream_enabled
32
+ raise StandardError, 'You are trying to use a block without stream enabled."'
33
+ end
34
+
35
+ results = []
36
+
37
+ response = Faraday.new.post do |request|
38
+ request.url url
39
+ request.headers['Content-Type'] = 'application/json'
40
+ request.headers['Authorization'] = "Bearer #{@authorizer.fetch_access_token!['access_token']}"
41
+ request.body = payload.to_json
42
+
43
+ if stream_enabled
44
+ parser = EventStreamParser::Parser.new
45
+
46
+ request.options.on_data = proc do |chunk, bytes, env|
47
+ if env && env.status != 200
48
+ raise_error = Faraday::Response::RaiseError.new
49
+ raise_error.on_complete(env.merge(body: chunk))
50
+ end
51
+
52
+ parser.feed(chunk) do |type, data, id, reconnection_time|
53
+ parsed_data = safe_parse_json(data)
54
+ result = {
55
+ event: safe_parse_json(data),
56
+ parsed: { type:, data:, id:, reconnection_time: },
57
+ raw: { chunk:, bytes:, env: }
58
+ }
59
+
60
+ callback.call(result[:event], result[:parsed], result[:raw]) unless callback.nil?
61
+
62
+ results << result
63
+
64
+ parsed_data['candidates'].find do |candidate|
65
+ !candidate['finishReason'].nil? && candidate['finishReason'] != ''
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ return safe_parse_json(response.body) unless stream_enabled
73
+
74
+ results.map { |result| result[:event] }
75
+ end
76
+
77
+ def safe_parse_json(raw)
78
+ raw.start_with?('{', '[') ? JSON.parse(raw) : raw
79
+ rescue JSON::ParserError
80
+ raw
81
+ end
82
+ end
83
+ end
84
+ end
data/gemini-ai.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'static/gem'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = Gemini::GEM[:name]
7
+ spec.version = Gemini::GEM[:version]
8
+ spec.authors = [Gemini::GEM[:author]]
9
+
10
+ spec.summary = Gemini::GEM[:summary]
11
+ spec.description = Gemini::GEM[:description]
12
+
13
+ spec.homepage = Gemini::GEM[:github]
14
+
15
+ spec.license = Gemini::GEM[:license]
16
+
17
+ spec.required_ruby_version = Gem::Requirement.new(">= #{Gemini::GEM[:ruby]}")
18
+
19
+ spec.metadata['allowed_push_host'] = Gemini::GEM[:gem_server]
20
+
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = Gemini::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 'event_stream_parser', '~> 1.0'
33
+ spec.add_dependency 'faraday', '~> 2.7', '>= 2.7.12'
34
+ spec.add_dependency 'googleauth', '~> 1.9', '>= 1.9.1'
35
+
36
+ spec.metadata['rubygems_mfa_required'] = 'true'
37
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../static/gem'
4
+ require_relative '../../controllers/client'
5
+
6
+ module Gemini
7
+ def self.new(...)
8
+ Controllers::Client.new(...)
9
+ end
10
+
11
+ def self.version
12
+ Gemini::GEM[:version]
13
+ end
14
+ end
data/static/gem.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemini
4
+ GEM = {
5
+ name: 'gemini-ai',
6
+ version: '1.0.0',
7
+ author: 'gbaptista',
8
+ summary: "Interact with Google's Gemini AI.",
9
+ description: "A Ruby Gem for interacting with Gemini through Vertex AI, Google's generative AI service.",
10
+ github: 'https://github.com/gbaptista/gemini-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."))