Skip to content

Commit bd4c38e

Browse files
committed
* use ruby_llm
1 parent c01b001 commit bd4c38e

4 files changed

Lines changed: 46 additions & 62 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ gem "nanoid"
4040

4141
# Integrations
4242
gem "httparty"
43+
gem "ruby_llm", "~> 1.9.0"
4344

4445
# Admin panel [https://docs.avohq.io/]
4546
gem "avo", ">= 3.2"

Gemfile.lock

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,24 @@ GEM
166166
erubi (1.13.1)
167167
et-orbi (1.2.11)
168168
tzinfo
169+
event_stream_parser (1.0.0)
169170
factory_bot (6.5.3)
170171
activesupport (>= 6.1.0)
171172
factory_bot_rails (6.4.4)
172173
factory_bot (~> 6.5)
173174
railties (>= 5.0.0)
174175
faker (3.5.1)
175176
i18n (>= 1.8.11, < 2)
177+
faraday (2.14.0)
178+
faraday-net_http (>= 2.0, < 3.5)
179+
json
180+
logger
181+
faraday-multipart (1.1.1)
182+
multipart-post (~> 2.0)
183+
faraday-net_http (3.4.1)
184+
net-http (>= 0.5.0)
185+
faraday-retry (2.3.2)
186+
faraday (~> 2.0)
176187
ferrum (0.15)
177188
addressable (~> 2.5)
178189
concurrent-ruby (~> 1.1)
@@ -283,8 +294,11 @@ GEM
283294
msgpack (1.8.0)
284295
multi_xml (0.7.2)
285296
bigdecimal (~> 3.1)
297+
multipart-post (2.4.1)
286298
mutex_m (0.3.0)
287299
nanoid (2.0.0)
300+
net-http (0.6.0)
301+
uri
288302
net-imap (0.5.11)
289303
date
290304
net-protocol
@@ -423,6 +437,17 @@ GEM
423437
rubocop (~> 1.72, >= 1.72.1)
424438
ruby-next-core (1.1.2)
425439
ruby-progressbar (1.13.0)
440+
ruby_llm (1.9.0)
441+
base64
442+
event_stream_parser (~> 1)
443+
faraday (>= 1.10.0)
444+
faraday-multipart (>= 1)
445+
faraday-net_http (>= 1)
446+
faraday-retry (>= 1)
447+
marcel (~> 1.0)
448+
ruby_llm-schema (~> 0.2.1)
449+
zeitwerk (~> 2)
450+
ruby_llm-schema (0.2.1)
426451
securerandom (0.4.1)
427452
sentry-rails (5.23.0)
428453
railties (>= 5.0)
@@ -535,6 +560,7 @@ DEPENDENCIES
535560
rails (= 8.1.0.rc1)
536561
rspec-rails
537562
rubocop-rspec
563+
ruby_llm (~> 1.9.0)
538564
sentry-rails (~> 5.15)
539565
sentry-ruby (~> 5.15)
540566
solid_queue

app/models/cloud/card_generator.rb

Lines changed: 15 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,35 @@
11
class Cloud
22
class CardGenerator
3-
FLASH_IMAGE_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent"
4-
53
private attr_reader :cloud, :api_key
64

75
delegate :participant, to: :cloud
86

9-
def initialize(cloud, api_key: GeminiConfig.api_key)
7+
def initialize(cloud)
108
@cloud = cloud
11-
@api_key = api_key
129
end
1310

1411
def generate
15-
raise ArgumentError, "Gemini API key not configured" if api_key.blank?
1612
raise ArgumentError, "No photo attached" unless cloud.image.attached?
1713

18-
parts = [
19-
{
20-
text: build_prompt
21-
},
22-
build_inline_data(cloud.image.blob.download, cloud.image.content_type),
23-
build_inline_data(Rails.public_path.join("sfruby_character.png").read, "image/png"),
24-
build_inline_data(Rails.public_path.join("sfruby_character_h.png").read, "image/png"),
25-
build_inline_data(Rails.public_path.join("sfruby_character_blue.png").read, "image/png")
26-
]
27-
28-
request = {
29-
contents: [
30-
{
31-
parts:
32-
}
33-
],
34-
generationConfig: {
35-
responseModalities: ["image"],
36-
temperature: 1.0
37-
}
38-
}
39-
40-
response = HTTParty.post(
41-
"#{FLASH_IMAGE_URL}?key=#{api_key}",
42-
body: request.to_json,
43-
headers: {"Content-Type" => "application/json"},
44-
timeout: 90
45-
)
46-
47-
if response.success?
48-
process_response(response.parsed_response)
49-
else
50-
raise "Gemini API error: #{response.code} - #{response.body}"
51-
end
52-
end
14+
chat = RubyLLM.chat(model: "gemini-2.5-flash-image")
15+
.with_temperature(1.0)
16+
.with_params(generationConfig: {responseModalities: ["image"]})
5317

54-
private
18+
response = chat.ask build_prompt, with: build_attachments
19+
raise "LLM error: #{response.raw.body.dig("candidates", 0, "finishMessage") || "no data"}" if response.content.blank?
5520

56-
def build_inline_data(io, content_type)
57-
{
58-
inlineData: {
59-
mimeType: content_type,
60-
data: Base64.strict_encode64(io)
61-
}
62-
}
21+
response.content[:attachments].first.source
6322
end
6423

65-
def process_response(response)
66-
parts = response.dig("candidates", 0, "content", "parts")
67-
68-
if parts.nil?
69-
raise "No parts found in response"
70-
end
71-
72-
image_part = parts.find { it.dig("inlineData", "mimeType")&.start_with?("image/") }
73-
74-
raise "No image data found in parts. Parts structure: #{parts.map(&:keys)}" unless image_part
75-
76-
image_data = image_part["inlineData"]["data"]
77-
decoded_image = Base64.decode64(image_data)
24+
private
7825

79-
StringIO.new(decoded_image)
26+
def build_attachments
27+
[
28+
cloud.image.blob,
29+
Rails.public_path.join("sfruby_character.png"),
30+
Rails.public_path.join("sfruby_character_h.png"),
31+
Rails.public_path.join("sfruby_character_blue.png")
32+
]
8033
end
8134

8235
def build_prompt

config/initializers/ruby_llm.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
RubyLLM.configure do |config|
2+
config.gemini_api_key = GeminiConfig.api_key
3+
config.log_level = Logger::DEBUG if ENV["DEBUG_LLM"].in?(%w[1 t true])
4+
end

0 commit comments

Comments
 (0)