gemini_cache 0.0.9 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae4847ce3abf808c2779601462af28a47989301593355a8a39977a5df495f322
4
- data.tar.gz: 71e72d0e2750d6b530d407f97a7490c65b6acbf342ad10803916b4b24d418b71
3
+ metadata.gz: c7e9a54d31ede523084b82ed3d94912bcbbd33ac2f0452bbc0a87d22ddc3d749
4
+ data.tar.gz: 7ca621fad09fc18f1a87938d2a42b289841ed7c9a6eaadd290869af4cd132613
5
5
  SHA512:
6
- metadata.gz: 8ab29a93e9555626f6d1eecb189351b5d62b28805e0e90e0ba9c29cb9e5bcf8698f72dadb0621ffbf196503d186a2c341b45c387bc25f940d055bb110455ff78
7
- data.tar.gz: 50a917b60662ecd5bc1cef90c0a633febfcfbe294c297525d397f60863dfaa29977e43c0743c9f717452fa7d45ce5fe2da8f11f3816d0f65fa5e06f6632675ff
6
+ metadata.gz: 7281d91213fa3b6243e6013e352653b89e15c0be1545ad62c11c97f3259f7afcdb673ffd82d9130c3fd61087ef555844ebe35f684c5ebb0ccd1e9fa0a9a65f7f
7
+ data.tar.gz: 9648a180a9c48e3ddddc52d3006603b9a8b8f418712474311c578660d8c329ceb4ca5ee23f89ab6262eb4058818e2289910dc58979702662852d50aa3ed2a85f
data/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # GeminiCache Module Usage Documentation
2
+
3
+ ## Introduction
4
+ The `GeminiCache` module is a library designed for managing API caches, with features to process web pages, local and remote files, as well as interact with an API to create, update, list, and delete caches. This document describes its functionalities and usage.
5
+
6
+ ## Requirements
7
+ To use the module, ensure the following Ruby libraries are installed:
8
+
9
+ - `faraday`
10
+ - `open-uri`
11
+ - `nokogiri`
12
+ - `json`
13
+ - `base64`
14
+
15
+ ## Code Structure
16
+
17
+ The module includes the following components:
18
+
19
+ 1. **Classes and Modules**:
20
+ - `GeminiCache::Error`: Class for handling custom errors.
21
+ - `GeminiCache`: Contains the main methods for cache management and file processing.
22
+
23
+ 2. **Dependencies**:
24
+ - `gemini_cache/configuration`
25
+ - `gemini_cache/api_client`
26
+ - `gemini_cache/item_extender`
27
+
28
+ ## Features
29
+
30
+ ### 1. HTML Parsing
31
+ Allows processing and cleaning the content of a web page.
32
+
33
+ #### Syntax
34
+ ```ruby
35
+ GeminiCache.parse_html(url:, default_remover: true)
36
+ ```
37
+ - **Parameters**:
38
+ - `url`: The URL of the page to process.
39
+ - `default_remover`: Automatically removes `<script>` and `<style>` elements (default: `true`).
40
+ - **Returns**: A `Nokogiri::HTML` object containing the processed HTML.
41
+
42
+ ### 2. File Reading
43
+
44
+ #### a) Local Files
45
+ Reads a local file and returns its Base64 encoded data.
46
+ ```ruby
47
+ GeminiCache.read_local_file(path:, mime_type:)
48
+ ```
49
+ - **Parameters**:
50
+ - `path`: Path to the file.
51
+ - `mime_type`: MIME type of the file.
52
+ - **Returns**: Hash containing the encoded data.
53
+
54
+ #### b) Remote Files
55
+ Reads a remote file and returns its Base64 encoded data.
56
+ ```ruby
57
+ GeminiCache.read_remote_file(url:, mime_type:)
58
+ ```
59
+ - **Parameters**:
60
+ - `url`: URL of the file.
61
+ - `mime_type`: MIME type of the file.
62
+ - **Returns**: Hash containing the encoded data.
63
+
64
+ ### 3. Webpage Text Reading
65
+ Extracts text content from a web page, removing unnecessary elements.
66
+ ```ruby
67
+ GeminiCache.read_webpage_text(url:, default_remover: true)
68
+ ```
69
+ - **Parameters**:
70
+ - `url`: URL of the page.
71
+ - `default_remover`: Automatically removes `<script>` and `<style>` elements (default: `true`).
72
+ - **Returns**: Hash containing the page text.
73
+
74
+ ### 4. Cache Creation
75
+ Creates a new cache from different data sources.
76
+
77
+ #### General Syntax
78
+ ```ruby
79
+ GeminiCache.create(parts:, display_name:, on_conflict: :raise_error, model: nil, ttl: nil)
80
+ ```
81
+ - **Parameters**:
82
+ - `parts`: Cache data.
83
+ - `display_name`: Display name of the cache.
84
+ - `on_conflict`: Action on conflict (`:raise_error` or `:get_existing`).
85
+ - `model`: Model used (default: system configuration).
86
+ - `ttl`: Time-to-live for the cache (default: system configuration).
87
+ - **Returns**: The created cache object.
88
+
89
+ #### Creation Methods
90
+ - Text:
91
+ ```ruby
92
+ GeminiCache.create_from_text(text:, **options)
93
+ ```
94
+ - Web Page:
95
+ ```ruby
96
+ GeminiCache.create_from_webpage(url:, **options)
97
+ ```
98
+ - Local File:
99
+ ```ruby
100
+ GeminiCache.create_from_local_file(path:, mime_type:, **options)
101
+ ```
102
+ - Remote File:
103
+ ```ruby
104
+ GeminiCache.create_from_remote_file(url:, mime_type:, **options)
105
+ ```
106
+
107
+ ### 5. Cache Management
108
+
109
+ #### Cache Listing
110
+ Lists all available caches.
111
+ ```ruby
112
+ GeminiCache.list
113
+ ```
114
+ - **Returns**: Array of cache objects.
115
+
116
+ #### Cache Retrieval
117
+ - By Name:
118
+ ```ruby
119
+ GeminiCache.find_by_name(name:)
120
+ ```
121
+ - By Display Name:
122
+ ```ruby
123
+ GeminiCache.find_by_display_name(display_name:)
124
+ ```
125
+
126
+ #### Cache Updating
127
+ Updates an existing cache.
128
+ ```ruby
129
+ GeminiCache.update(name:, content:)
130
+ ```
131
+ - **Parameters**:
132
+ - `name`: Name of the cache.
133
+ - `content`: Updated content.
134
+
135
+ #### Cache Deletion
136
+ - By Name:
137
+ ```ruby
138
+ GeminiCache.delete(name:)
139
+ ```
140
+ - All Caches:
141
+ ```ruby
142
+ GeminiCache.delete_all
143
+ ```
144
+
145
+ ## Configuration
146
+ The methods use configurations defined in the `gemini_cache/configuration` module, and communication is handled via `gemini_cache/api_client`.
147
+
148
+ ## Errors
149
+ In case of conflicts or API errors, the module raises a custom exception `GeminiCache::Error`.
150
+
151
+ ## Conclusion
152
+ This documentation covers the main functionalities of the `GeminiCache` module. For more details on specific configurations, refer to the source code or official documentation.
153
+
@@ -0,0 +1,61 @@
1
+ module GeminiCache
2
+ class ApiClient
3
+ class ApiError < StandardError; end
4
+
5
+ def initialize
6
+ @conn = Faraday.new(
7
+ url: GeminiCache.configuration.api_base_url,
8
+ headers: { 'Content-Type' => 'application/json' }
9
+ )
10
+ end
11
+
12
+ def create_cache(content)
13
+ response = @conn.post('/v1beta/cachedContents') do |req|
14
+ req.params['key'] = api_key
15
+ req.body = content
16
+ end
17
+
18
+ handle_response(response)
19
+ end
20
+
21
+ def list_caches
22
+ response = @conn.get('/v1beta/cachedContents') do |req|
23
+ req.params['key'] = api_key
24
+ end
25
+
26
+ handle_response(response)
27
+ end
28
+
29
+ def update_cache(name, content)
30
+ response = @conn.patch("/v1beta/#{name}") do |req|
31
+ req.params['key'] = api_key
32
+ req.body = content
33
+ end
34
+
35
+ handle_response(response)
36
+ end
37
+
38
+ def delete_cache(name)
39
+ response = @conn.delete("/v1beta/#{name}") do |req|
40
+ req.params['key'] = api_key
41
+ end
42
+
43
+ handle_response(response)
44
+ end
45
+
46
+ private
47
+
48
+ def api_key
49
+ GeminiCache.configuration.api_key || ENV.fetch('GEMINI_API_KEY')
50
+ end
51
+
52
+ def handle_response(response)
53
+ return JSON.parse(response.body) if response.status == 200
54
+
55
+ error_message = JSON.parse(response.body)['error'] rescue response.body
56
+ raise ApiError, "API request failed (#{response.status}): #{error_message}"
57
+ rescue Faraday::Error => e
58
+ raise ApiError, "Network error: #{e.message}"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ module GeminiCache
2
+ class Configuration
3
+ attr_accessor :api_key, :api_base_url, :default_model, :default_ttl
4
+
5
+ def initialize
6
+ @api_base_url = 'https://generativelanguage.googleapis.com'
7
+ @default_model = 'gemini-1.5-flash-8b'
8
+ @default_ttl = 300
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def configure
18
+ yield(configuration)
19
+ end
20
+ end
21
+ end
@@ -1,46 +1,73 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ItemExtender
4
+ GEMINI_API_BASE_URL = 'https://generativelanguage.googleapis.com'
5
+ DEFAULT_TIMEOUT = 300 # seconds
6
+ ACCURATE_MODE_CONFIG = { temperature: 0, topP: 0, topK: 1 }.freeze
7
+
2
8
  def delete = GeminiCache.delete(name: self['name'])
3
9
 
4
10
  def ttl=(new_ttl)
5
- GeminiCache.update(name: self['name'], content: { ttl: "#{new_ttl}s" })
6
- GeminiCache.get_by_name(name: self['name'])
11
+ GeminiCache.update(name: self['name'], content: { ttl: "#{new_ttl}s" }.to_json)
7
12
  end
8
-
13
+
9
14
  def generate_content(contents:, generation_config: nil)
10
- conn = Faraday.new(
11
- url: 'https://generativelanguage.googleapis.com',
15
+ response = api_client.post(generate_content_endpoint) do |req|
16
+ req.params['key'] = ENV.fetch('GEMINI_API_KEY')
17
+ req.body = build_request_body(contents, generation_config)
18
+ end
19
+
20
+ handle_response(response)
21
+ rescue Faraday::Error => e
22
+ raise GeminiAPIError, "Request failed: #{e.message}"
23
+ end
24
+
25
+ def single_prompt(prompt:, generation_config: :accurate_mode)
26
+ config = generation_config.eql?(:accurate_mode) ? ACCURATE_MODE_CONFIG : generation_config
27
+
28
+ generate_content(
29
+ contents: [{ parts: [{ text: prompt }], role: 'user' }],
30
+ generation_config: config
31
+ ).content
32
+ end
33
+
34
+ private
35
+
36
+ def api_client
37
+ @api_client ||= Faraday.new(
38
+ url: GEMINI_API_BASE_URL,
12
39
  headers: { 'Content-Type' => 'application/json' }
13
40
  ) do |f|
14
- f.options.timeout = 300 # timeout em segundos para a requisição completa
15
- f.options.open_timeout = 300 # timeout em segundos para abrir a conexão
41
+ f.options.timeout = DEFAULT_TIMEOUT
42
+ f.options.open_timeout = DEFAULT_TIMEOUT
16
43
  end
44
+ end
45
+
46
+ def generate_content_endpoint
47
+ "/v1beta/models/#{self['model'].split('/').last}:generateContent"
48
+ end
17
49
 
18
- body = {
50
+ def build_request_body(contents, generation_config)
51
+ {
19
52
  cached_content: self['name'],
20
- contents:
21
- }
53
+ contents: contents,
54
+ generation_config: generation_config
55
+ }.compact.to_json
56
+ end
22
57
 
23
- body[:generation_config] = generation_config if !generation_config.nil?
58
+ def handle_response(response)
59
+ return parse_successful_response(response) if response.status == 200
24
60
 
25
- response = conn.post("/v1beta/models/#{self['model'].split('/').last}:generateContent") do |req|
26
- req.params['key'] = ENV.fetch('GEMINI_API_KEY')
27
- req.body = body.to_json
28
- end
29
-
30
- if response.status == 200
31
- resp = JSON.parse(response.body)
32
- def resp.content = dig('candidates', 0, 'content', 'parts', 0, 'text')
33
- return resp
34
- end
61
+ raise GeminiAPIError, "Content generation failed: #{response.body}"
62
+ end
35
63
 
36
- raise "Erro ao gerar conteúdo: #{response.body}"
37
- rescue Faraday::Error => e
38
- raise "Erro na requisição: #{e.message}"
64
+ def parse_successful_response(response)
65
+ resp = JSON.parse(response.body)
66
+ def resp.content
67
+ dig('candidates', 0, 'content', 'parts', 0, 'text')
68
+ end
69
+ resp
39
70
  end
40
-
41
- def single_prompt(prompt:, generation_config: :accurate_mode)
42
- # accurate_mode: less creative, more accurate
43
- generation_config = { temperature: 0, topP: 0, topK: 1 } if generation_config.eql?(:accurate_mode)
44
- generate_content(contents: [{ parts: [{ text: prompt }], role: 'user' }], generation_config:).content
45
- end
46
71
  end
72
+
73
+ class GeminiAPIError < StandardError; end
data/lib/gemini_cache.rb CHANGED
@@ -4,122 +4,89 @@ require 'nokogiri'
4
4
  require 'json'
5
5
  require 'base64'
6
6
 
7
+ require 'gemini_cache/configuration'
8
+ require 'gemini_cache/api_client'
7
9
  require 'gemini_cache/item_extender'
8
10
 
9
11
  module GeminiCache
10
- def self.read_local_file(path:, mime_type:) = { inline_data: { mime_type:, data: Base64.strict_encode64(File.read(path)) } }
11
- def self.read_remote_file(url:, mime_type:) = { inline_data: { mime_type:, data: Base64.strict_encode64(URI.open(url).read) } }
12
- def self.read_html(url:, default_remover: true)
13
- doc = Nokogiri::HTML(URI.open(url))
14
- %w[script style].each { |element| doc.css(element).each(&:remove) } if default_remover
12
+ class Error < StandardError; end
15
13
 
16
- doc
17
- end
18
-
19
- def self.create(parts:, display_name:, model: 'gemini-1.5-flash-8b', ttl: 300)
20
- raise "Cache name already exist: '#{display_name}'" if GeminiCache.get_by_display_name(display_name:)
21
-
22
- content = {
23
- model: "models/#{model}",
24
- display_name:,
25
- contents: [parts:, role: 'user'],
26
- ttl: "#{ttl}s"
27
- }.to_json
28
-
29
- conn = Faraday.new(
30
- url: 'https://generativelanguage.googleapis.com',
31
- headers: { 'Content-Type' => 'application/json' }
32
- )
33
-
34
- response = conn.post('/v1beta/cachedContents') do |req|
35
- req.params['key'] = ENV.fetch('GEMINI_API_KEY')
36
- req.body = content
14
+ class << self
15
+ def parse_html(url:, default_remover: true)
16
+ doc = Nokogiri::HTML(URI.open(url, "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
17
+ %w[script style].each { |element| doc.css(element).each(&:remove) } if default_remover
18
+ doc
37
19
  end
38
-
39
- return get_by_name(name: JSON.parse(response.body)['name']) if response.status == 200
40
-
41
- raise "Erro ao criar cache: #{response.status} - #{response.body}"
42
- rescue Faraday::Error => e
43
- raise "Erro na requisição: #{e.message}"
44
- end
45
-
46
- def self.create_from_text(text:, display_name:, model: 'gemini-1.5-flash-8b', ttl: 300)
47
- GeminiCache.create(parts: [{ text: }], display_name:, model:, ttl:)
48
- end
49
20
 
50
- def self.create_from_webpage(url:, display_name:, model: 'gemini-1.5-flash-8b', ttl: 300)
51
- create_from_text(text: GeminiCache.read_html(url:).inner_text, display_name:, model:, ttl:)
52
- end
53
-
54
- def self.create_from_local_file(path:, mime_type:, display_name:, model: 'gemini-1.5-flash-8b', ttl: 300)
55
- GeminiCache.create(parts: GeminiCache.read_local_file(path:, mime_type:), display_name:, model:, ttl:)
56
- end
21
+ def read_local_file(path:, mime_type:)
22
+ { inline_data: { mime_type:, data: Base64.strict_encode64(File.read(path)) } }
23
+ end
57
24
 
58
- def self.create_from_remote_file(url:, mime_type:, display_name:, model: 'gemini-1.5-flash-8b', ttl: 300)
59
- GeminiCache.create(parts: GeminiCache.read_remote_file(url:, mime_type:), display_name:, model:, ttl:)
60
- end
25
+ def read_remote_file(url:, mime_type:)
26
+ { inline_data: { mime_type:, data: Base64.strict_encode64(URI.open(url).read) } }
27
+ end
61
28
 
62
- def self.get_by_name(name: nil) = GeminiCache.list.find { |item| item['name'].eql? name }
63
- def self.get_by_display_name(display_name: nil) = GeminiCache.list.find { |item| item['displayName'].eql? display_name }
29
+ def read_webpage_text(url:, default_remover: true) = { text: parse_html(url:, default_remover:).inner_text }
30
+
31
+ def create(parts:, display_name:, on_conflict: :raise_error, model: nil, ttl: nil)
32
+ existing_cache = find_by_display_name(display_name:)
33
+
34
+ if existing_cache
35
+ case on_conflict
36
+ when :raise_error
37
+ raise Error, "Cache with display name '#{display_name}' already exists"
38
+ when :get_existing
39
+ return existing_cache
40
+ end
41
+ end
42
+
43
+ content = {
44
+ model: "models/#{model || configuration.default_model}",
45
+ display_name: display_name,
46
+ contents: [{ parts:, role: 'user' }],
47
+ ttl: "#{ttl || configuration.default_ttl}s"
48
+ }
49
+
50
+ response = api_client.create_cache(content.to_json)
51
+ find_by_name(name: response['name'])
52
+ end
64
53
 
65
- def self.list
66
- conn = Faraday.new(
67
- url: 'https://generativelanguage.googleapis.com',
68
- headers: { 'Content-Type' => 'application/json' }
69
- )
54
+ def create_from_text(text:, **options) = create(parts: [{ text: }], **options)
55
+ def create_from_webpage(url:, **options) = create_from_text(text: read_webpage_text(url:)[:text], **options)
56
+ def create_from_local_file(path:, mime_type:, **options)
57
+ file_data = read_local_file(path: path, mime_type: mime_type)
58
+ create(parts: [file_data], **options)
59
+ end
70
60
 
71
- response = conn.get("/v1beta/cachedContents") do |req|
72
- req.params['key'] = ENV.fetch('GEMINI_API_KEY')
61
+ def create_from_remote_file(url:, mime_type:, **options)
62
+ file_data = read_remote_file(url: url, mime_type: mime_type)
63
+ create(parts: [file_data], **options)
73
64
  end
74
-
75
- return [] if JSON.parse(response.body).empty?
76
65
 
77
- JSON.parse(response.body)['cachedContents'].map { |item| item.extend(ItemExtender) }
66
+ def list
67
+ response = api_client.list_caches
68
+ return [] if response.empty?
78
69
 
79
- rescue Faraday::Error => e
80
- raise "Erro na requisição: #{e.message}"
81
- end
82
-
83
- def self.update(name:, content:)
84
- conn = Faraday.new(
85
- url: 'https://generativelanguage.googleapis.com',
86
- headers: { 'Content-Type' => 'application/json' }
87
- )
88
-
89
- response = conn.patch("/v1beta/#{name}") do |req|
90
- req.params['key'] = ENV.fetch('GEMINI_API_KEY')
91
- req.body = content.to_json
70
+ response['cachedContents'].map { |item| item.extend(ItemExtender) }
92
71
  end
93
72
 
94
- return JSON.parse(response.body) if response.status == 200
95
-
96
- raise "Erro ao atualizar cache: #{response.body}"
97
- rescue Faraday::Error => e
98
- raise "Erro na requisição: #{e.message}"
99
- end
73
+ def find_by_name(name:) = list.find { |item| item['name'].eql?(name) }
74
+ def find_by_display_name(display_name:) = list.find { |item| item['displayName'].eql?(display_name) }
100
75
 
101
- def self.delete(name:)
102
- conn = Faraday.new(
103
- url: 'https://generativelanguage.googleapis.com',
104
- headers: { 'Content-Type' => 'application/json' }
105
- )
76
+ def update(name:, content:) = api_client.update_cache(name, content)
106
77
 
107
- response = conn.delete("/v1beta/#{name}") do |req|
108
- req.params['key'] = ENV.fetch('GEMINI_API_KEY')
78
+ def delete(name:)
79
+ api_client.delete_cache(name)
80
+ true
109
81
  end
110
82
 
111
- return true if response.status == 200
112
-
113
- raise "Erro ao deletar cache: #{response.body}"
114
- rescue Faraday::Error => e
115
- raise "Erro na requisição: #{e.message}"
116
- end
83
+ def delete_all() = list.each { |item| item.delete }
84
+ alias clear delete_all
117
85
 
118
- def self.delete_all
119
- GeminiCache.list.each { |item| item.delete }
120
- end
86
+ private
121
87
 
122
- class << self
123
- alias clear delete_all
88
+ def api_client
89
+ @api_client ||= ApiClient.new
90
+ end
124
91
  end
125
92
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemini_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gedean Dias
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-23 00:00:00.000000000 Z
10
+ date: 2025-01-06 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -58,13 +57,15 @@ executables: []
58
57
  extensions: []
59
58
  extra_rdoc_files: []
60
59
  files:
60
+ - README.md
61
61
  - lib/gemini_cache.rb
62
+ - lib/gemini_cache/api_client.rb
63
+ - lib/gemini_cache/configuration.rb
62
64
  - lib/gemini_cache/item_extender.rb
63
65
  homepage: https://github.com/gedean/gemini_cache
64
66
  licenses:
65
67
  - MIT
66
68
  metadata: {}
67
- post_install_message:
68
69
  rdoc_options: []
69
70
  require_paths:
70
71
  - lib
@@ -79,8 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
82
  requirements: []
82
- rubygems_version: 3.5.23
83
- signing_key:
83
+ rubygems_version: 3.6.2
84
84
  specification_version: 4
85
85
  summary: Ruby Gemini Context Caching
86
86
  test_files: []